import { JSONSchema7 } from 'json-schema';
import { randomUUID } from 'crypto';
import { promises as fs } from 'fs';
import path from 'path';
import { createTool, createSuccessResult, createErrorResult } from '../../core/tool-framework.js';
import { ToolRegistration, RequestContext } from '../../core/types.js';

/**
 * Workspace Module Tools - 12-Factor MCP Implementation
 * 
 * Manages multi-repository workspaces with:
 * - Automatic repository detection
 * - Primary repository designation
 * - Active workspace tracking
 * - Repository relationship management
 */

// Input type interfaces
interface CreateWorkspaceInput {
  name: string;
  description?: string;
  rootPath?: string;
  autoDetect?: boolean;
  primaryRepoPath?: string;
}

interface AddRepositoryInput {
  workspaceId: string;
  name?: string;
  path: string;
  type?: 'git' | 'svn' | 'mercurial' | 'local';
  setPrimary?: boolean;
  remote?: string;
  branch?: string;
}

interface ListRepositoriesInput {
  workspaceId: string;
}

interface SwitchWorkspaceInput {
  workspaceId: string;
}

/**
 * Check if a directory is a git repository
 */
async function isGitRepository(dirPath: string): Promise<boolean> {
  try {
    await fs.access(path.join(dirPath, '.git'));
    return true;
  } catch {
    return false;
  }
}

/**
 * Auto-detect repositories in a directory
 */
async function detectRepositories(rootPath: string): Promise<Array<{
  name: string;
  path: string;
  type: 'git' | 'svn' | 'mercurial' | 'local';
}>> {
  const repositories = [];
  
  try {
    const entries = await fs.readdir(rootPath, { withFileTypes: true });
    
    for (const entry of entries) {
      if (entry.isDirectory() && !entry.name.startsWith('.')) {
        const fullPath = path.join(rootPath, entry.name);
        
        // Check for version control systems
        let repoType: 'git' | 'svn' | 'mercurial' | 'local' = 'local';
        
        if (await isGitRepository(fullPath)) {
          repoType = 'git';
        } else {
          // Check for other VCS
          try {
            await fs.access(path.join(fullPath, '.svn'));
            repoType = 'svn';
          } catch {
            try {
              await fs.access(path.join(fullPath, '.hg'));
              repoType = 'mercurial';
            } catch {
              // Check if it's a Node.js package
              try {
                await fs.access(path.join(fullPath, 'package.json'));
                repoType = 'local';
              } catch {
                // Skip non-repository directories
                continue;
              }
            }
          }
        }
        
        repositories.push({
          name: entry.name,
          path: fullPath,
          type: repoType
        });
      }
    }
  } catch (error) {
    console.error('Error detecting repositories:', error);
  }
  
  return repositories;
}

/**
 * Create a new workspace
 */
const createWorkspaceTool = createTool<CreateWorkspaceInput, any>({
  name: 'create_workspace',
  description: 'Create a new multi-repository workspace',
  category: 'workspace',
  inputSchema: {
    type: 'object',
    properties: {
      name: {
        type: 'string',
        description: 'Workspace name',
        minLength: 1,
        maxLength: 200
      },
      description: {
        type: 'string',
        description: 'Workspace description',
        maxLength: 1000
      },
      rootPath: {
        type: 'string',
        description: 'Root directory path (defaults to current directory)'
      },
      autoDetect: {
        type: 'boolean',
        description: 'Automatically detect repositories',
        default: true
      },
      primaryRepoPath: {
        type: 'string',
        description: 'Path to the primary repository'
      }
    },
    required: ['name'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: CreateWorkspaceInput, context: RequestContext) {
    try {
      const rootPath = input.rootPath || process.cwd();
      
      // Verify root path exists
      try {
        await fs.access(rootPath);
      } catch {
        return createErrorResult({
          code: 'INVALID_PATH',
          message: 'Root path does not exist',
          details: { rootPath },
          category: 'validation'
        });
      }

      // Resolve absolute path
      const absoluteRootPath = path.resolve(rootPath);

      // Check for duplicate workspace names
      const existingWorkspace = await context.db.get(
        'SELECT id FROM workspaces WHERE name = ?',
        [input.name]
      );

      if (existingWorkspace.success && existingWorkspace.data) {
        return createErrorResult({
          code: 'DUPLICATE_RESOURCE',
          message: 'A workspace with this name already exists',
          category: 'validation'
        });
      }

      const workspaceId = `ws-${randomUUID()}`;
      const now = Date.now();

      // Create workspace
      const result = await context.db.run(
        `INSERT INTO workspaces 
         (id, name, description, root_path, active, settings, created_at, updated_at) 
         VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
        [
          workspaceId,
          input.name,
          input.description || '',
          absoluteRootPath,
          false, // Not active by default
          JSON.stringify({}),
          now,
          now
        ]
      );

      if (!result.success) {
        return createErrorResult({
          code: 'DATABASE_ERROR',
          message: 'Failed to create workspace',
          details: { error: result.error },
          category: 'system'
        });
      }

      // Auto-detect repositories if requested
      const detectedRepos = [];
      if (input.autoDetect !== false) {
        const repos = await detectRepositories(absoluteRootPath);
        
        for (const repo of repos) {
          const repoId = `repo-${randomUUID()}`;
          const isPrimary = repo.path === input.primaryRepoPath || 
                          (repos.length === 1 && !input.primaryRepoPath);
          
          await context.db.run(
            `INSERT INTO workspace_repositories 
             (id, workspace_id, name, path, type, is_primary, created_at, updated_at) 
             VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
            [
              repoId,
              workspaceId,
              repo.name,
              repo.path,
              repo.type,
              isPrimary,
              now,
              now
            ]
          );
          
          detectedRepos.push({
            id: repoId,
            name: repo.name,
            path: repo.path,
            type: repo.type,
            primary: isPrimary
          });
        }
      }

      // If no active workspace exists, make this one active
      const activeWorkspaceCheck = await context.db.get(
        'SELECT id FROM workspaces WHERE active = TRUE'
      );

      if (!activeWorkspaceCheck.success || !activeWorkspaceCheck.data) {
        await context.db.run(
          'UPDATE workspaces SET active = TRUE WHERE id = ?',
          [workspaceId]
        );
      }

      return createSuccessResult({
        workspace: {
          id: workspaceId,
          name: input.name,
          description: input.description || '',
          rootPath: absoluteRootPath,
          active: !activeWorkspaceCheck.data,
          repositories: detectedRepos,
          createdAt: new Date(now).toISOString()
        },
        message: `Workspace "${input.name}" created successfully`,
        detectedRepositories: detectedRepos.length,
        nextSteps: [
          detectedRepos.length === 0 ? 'Add repositories to your workspace' : null,
          !detectedRepos.some(r => r.primary) ? 'Designate a primary repository' : null,
          'Switch to this workspace to make it active'
        ].filter(Boolean)
      });

    } catch (error) {
      return createErrorResult({
        code: 'EXECUTION_ERROR',
        message: `Failed to create workspace: ${error instanceof Error ? error.message : 'Unknown error'}`,
        category: 'execution'
      });
    }
  }
});

/**
 * Add a repository to a workspace
 */
const addRepositoryTool = createTool<AddRepositoryInput, any>({
  name: 'add_repository',
  description: 'Add a repository to an existing workspace',
  category: 'workspace',
  inputSchema: {
    type: 'object',
    properties: {
      workspaceId: {
        type: 'string',
        description: 'Workspace ID',
        pattern: '^ws-[a-f0-9-]+$'
      },
      name: {
        type: 'string',
        description: 'Repository name (defaults to directory name)',
        maxLength: 200
      },
      path: {
        type: 'string',
        description: 'Repository path'
      },
      type: {
        type: 'string',
        enum: ['git', 'svn', 'mercurial', 'local'],
        default: 'local',
        description: 'Repository type'
      },
      setPrimary: {
        type: 'boolean',
        description: 'Set as primary repository',
        default: false
      },
      remote: {
        type: 'string',
        description: 'Remote repository URL'
      },
      branch: {
        type: 'string',
        description: 'Current branch name'
      }
    },
    required: ['workspaceId', 'path'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: AddRepositoryInput, context: RequestContext) {
    try {
      // Verify workspace exists
      const workspaceResult = await context.db.get(
        'SELECT id, name FROM workspaces WHERE id = ?',
        [input.workspaceId]
      );

      if (!workspaceResult.success || !workspaceResult.data) {
        return createErrorResult({
          code: 'RESOURCE_NOT_FOUND',
          message: 'Workspace not found',
          category: 'validation'
        });
      }

      const workspace = workspaceResult.data;

      // Verify repository path exists
      try {
        await fs.access(input.path);
      } catch {
        return createErrorResult({
          code: 'INVALID_PATH',
          message: 'Repository path does not exist',
          details: { path: input.path },
          category: 'validation'
        });
      }

      const absolutePath = path.resolve(input.path);
      const repoName = input.name || path.basename(absolutePath);

      // Check if repository already exists in workspace
      const existingRepo = await context.db.get(
        'SELECT id FROM workspace_repositories WHERE workspace_id = ? AND path = ?',
        [input.workspaceId, absolutePath]
      );

      if (existingRepo.success && existingRepo.data) {
        return createErrorResult({
          code: 'DUPLICATE_RESOURCE',
          message: 'Repository already exists in this workspace',
          category: 'validation'
        });
      }

      // Auto-detect repository type if not specified
      let repoType = input.type || 'local';
      if (repoType === 'local') {
        if (await isGitRepository(absolutePath)) {
          repoType = 'git';
        }
      }

      const repoId = `repo-${randomUUID()}`;
      const now = Date.now();

      // If setting as primary, unset other primary repos
      if (input.setPrimary) {
        await context.db.run(
          'UPDATE workspace_repositories SET is_primary = FALSE WHERE workspace_id = ?',
          [input.workspaceId]
        );
      }

      // Add repository
      const result = await context.db.run(
        `INSERT INTO workspace_repositories 
         (id, workspace_id, name, path, type, is_primary, remote_url, current_branch, 
          created_at, updated_at) 
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
        [
          repoId,
          input.workspaceId,
          repoName,
          absolutePath,
          repoType,
          input.setPrimary || false,
          input.remote || null,
          input.branch || null,
          now,
          now
        ]
      );

      if (!result.success) {
        return createErrorResult({
          code: 'DATABASE_ERROR',
          message: 'Failed to add repository',
          details: { error: result.error },
          category: 'system'
        });
      }

      // Update workspace modification time
      await context.db.run(
        'UPDATE workspaces SET updated_at = ? WHERE id = ?',
        [now, input.workspaceId]
      );

      return createSuccessResult({
        repository: {
          id: repoId,
          workspaceId: input.workspaceId,
          name: repoName,
          path: absolutePath,
          type: repoType,
          primary: input.setPrimary || false,
          remote: input.remote || null,
          branch: input.branch || null,
          createdAt: new Date(now).toISOString()
        },
        workspace: {
          id: workspace.id,
          name: workspace.name
        },
        message: `Repository "${repoName}" added to workspace "${workspace.name}"`,
        detectedType: repoType !== (input.type || 'local'),
        nextSteps: input.setPrimary ? [] : ['Consider setting this as the primary repository']
      });

    } catch (error) {
      return createErrorResult({
        code: 'EXECUTION_ERROR',
        message: `Failed to add repository: ${error instanceof Error ? error.message : 'Unknown error'}`,
        category: 'execution'
      });
    }
  }
});

/**
 * List repositories in a workspace
 */
const listRepositoriesTool = createTool<ListRepositoriesInput, any>({
  name: 'list_repositories',
  description: 'List all repositories in a workspace',
  category: 'workspace',
  readOnly: true,
  inputSchema: {
    type: 'object',
    properties: {
      workspaceId: {
        type: 'string',
        description: 'Workspace ID',
        pattern: '^ws-[a-f0-9-]+$'
      }
    },
    required: ['workspaceId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: ListRepositoriesInput, context: RequestContext) {
    try {
      // Get workspace details
      const workspaceResult = await context.db.get(
        'SELECT * FROM workspaces WHERE id = ?',
        [input.workspaceId]
      );

      if (!workspaceResult.success || !workspaceResult.data) {
        return createErrorResult({
          code: 'RESOURCE_NOT_FOUND',
          message: 'Workspace not found',
          category: 'validation'
        });
      }

      const workspace = workspaceResult.data;

      // Get all repositories
      const reposResult = await context.db.query(
        'SELECT * FROM workspace_repositories WHERE workspace_id = ? ORDER BY is_primary DESC, name',
        [input.workspaceId]
      );

      if (!reposResult.success) {
        return createErrorResult({
          code: 'DATABASE_ERROR',
          message: 'Failed to list repositories',
          details: { error: reposResult.error },
          category: 'system'
        });
      }

      const repositories = (reposResult.data || []).map((repo: any) => ({
        id: repo.id,
        name: repo.name,
        path: repo.path,
        type: repo.type,
        primary: repo.is_primary,
        remote: repo.remote_url,
        branch: repo.current_branch,
        lastSync: repo.last_sync ? new Date(repo.last_sync).toISOString() : null,
        dependencies: JSON.parse(repo.dependencies || '[]')
      }));

      // Get repository type breakdown
      const typeBreakdown = repositories.reduce((acc: any, repo: any) => {
        acc[repo.type] = (acc[repo.type] || 0) + 1;
        return acc;
      }, {});

      return createSuccessResult({
        workspace: {
          id: workspace.id,
          name: workspace.name,
          description: workspace.description,
          rootPath: workspace.root_path,
          active: workspace.active,
          createdAt: new Date(workspace.created_at).toISOString(),
          updatedAt: new Date(workspace.updated_at).toISOString()
        },
        repositories,
        statistics: {
          total: repositories.length,
          byType: typeBreakdown,
          primaryRepository: repositories.find((r: any) => r.primary)?.name || null
        }
      });

    } catch (error) {
      return createErrorResult({
        code: 'EXECUTION_ERROR',
        message: `Failed to list repositories: ${error instanceof Error ? error.message : 'Unknown error'}`,
        category: 'execution'
      });
    }
  }
});

/**
 * Switch active workspace
 */
const switchWorkspaceTool = createTool<SwitchWorkspaceInput, any>({
  name: 'switch_workspace',
  description: 'Switch the active workspace',
  category: 'workspace',
  inputSchema: {
    type: 'object',
    properties: {
      workspaceId: {
        type: 'string',
        description: 'Workspace ID to switch to',
        pattern: '^ws-[a-f0-9-]+$'
      }
    },
    required: ['workspaceId'],
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: SwitchWorkspaceInput, context: RequestContext) {
    try {
      // Verify workspace exists
      const workspaceResult = await context.db.get(
        'SELECT * FROM workspaces WHERE id = ?',
        [input.workspaceId]
      );

      if (!workspaceResult.success || !workspaceResult.data) {
        return createErrorResult({
          code: 'RESOURCE_NOT_FOUND',
          message: 'Workspace not found',
          category: 'validation'
        });
      }

      const workspace = workspaceResult.data;

      // If already active, no-op
      if (workspace.active) {
        return createSuccessResult({
          workspace: {
            id: workspace.id,
            name: workspace.name,
            active: true
          },
          message: `Workspace "${workspace.name}" is already active`,
          changed: false
        });
      }

      // Deactivate all workspaces
      await context.db.run(
        'UPDATE workspaces SET active = FALSE'
      );

      // Activate target workspace
      const now = Date.now();
      await context.db.run(
        'UPDATE workspaces SET active = TRUE, updated_at = ? WHERE id = ?',
        [now, input.workspaceId]
      );

      // Get repository count
      const repoCountResult = await context.db.get(
        'SELECT COUNT(*) as count FROM workspace_repositories WHERE workspace_id = ?',
        [input.workspaceId]
      );

      return createSuccessResult({
        workspace: {
          id: workspace.id,
          name: workspace.name,
          description: workspace.description,
          rootPath: workspace.root_path,
          active: true,
          repositoryCount: repoCountResult.data?.count || 0
        },
        message: `Switched to workspace "${workspace.name}"`,
        changed: true,
        previousActiveWorkspace: null // Could track this if needed
      });

    } catch (error) {
      return createErrorResult({
        code: 'EXECUTION_ERROR',
        message: `Failed to switch workspace: ${error instanceof Error ? error.message : 'Unknown error'}`,
        category: 'execution'
      });
    }
  }
});

/**
 * Setup workspace tools
 */
export async function setupWorkspaceTools(): Promise<ToolRegistration> {
  return {
    module: 'workspace',
    tools: [
      createWorkspaceTool,
      addRepositoryTool,
      listRepositoriesTool,
      switchWorkspaceTool
    ]
  };
}