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

/**
 * Project Management Tools - 12-Factor MCP Implementation
 * 
 * Implements Factor 2: Deterministic Execution with structured outputs
 * Implements Factor 3: Stateless Processes with RequestContext
 * Implements Factor 4: Structured Outputs for LLM consumption
 */

// Input type interfaces
interface InitProjectInput {
  projectName?: string;
}

interface CheckProjectStatusInput {
  // No parameters needed
}

interface FindProjectInput {
  // No parameters needed
}

interface RemoveProjectInput {
  confirm?: boolean;
}

interface UpdateProjectConfigInput {
  name?: string;
  description?: string;
  settings?: Record<string, any>;
}

interface ProjectMarker {
  projectId: string;
  projectName: string;
  createdAt: string;
  atlasVersion: string;
  projectRoot: string;
}

/**
 * Initialize Atlas project
 */
const initAtlasTool = createTool<InitProjectInput, any>({
  name: 'init_atlas',
  description: 'Initialize Atlas MCP in the current project',
  category: 'project-management',
  inputSchema: {
    type: 'object',
    properties: {
      projectName: {
        type: 'string',
        description: 'Custom project name (defaults to directory name)',
        maxLength: 200
      }
    },
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: InitProjectInput, context: RequestContext) {
    try {
      const projectId = randomUUID();
      const now = Date.now();
      const projectRoot = process.cwd();
      const projectName = input.projectName || path.basename(projectRoot);

      // Check if project already exists
      const existingProject = await context.db.get(
        'SELECT * FROM projects WHERE id = ? OR name = ?',
        [projectId, projectName]
      );

      if (existingProject.data) {
        return createSuccessResult({
          alreadyInitialized: true,
          project: existingProject.data,
          message: `Project "${projectName}" already initialized`
        });
      }

      // Create project in database
      const result = await context.db.run(
        `INSERT INTO projects (id, name, description, config, created_at, updated_at) 
         VALUES (?, ?, ?, ?, ?, ?)`,
        [
          projectId,
          projectName,
          `Atlas MCP project: ${projectName}`,
          JSON.stringify({
            projectRoot,
            atlasVersion: '1.0.0',
            initializedAt: now
          }),
          now,
          now
        ]
      );

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

      // Create .atlas marker file
      const markerPath = path.join(projectRoot, '.atlas');
      const marker: ProjectMarker = {
        projectId,
        projectName,
        createdAt: new Date(now).toISOString(),
        atlasVersion: '1.0.0',
        projectRoot
      };

      await fs.writeFile(markerPath, JSON.stringify(marker, null, 2), 'utf-8');

      // Add .atlas to gitignore if git exists
      try {
        const gitignorePath = path.join(projectRoot, '.gitignore');
        let gitignoreContent = '';
        try {
          gitignoreContent = await fs.readFile(gitignorePath, 'utf-8');
        } catch {
          // .gitignore doesn't exist yet
        }

        if (!gitignoreContent.includes('.atlas')) {
          gitignoreContent += (gitignoreContent && !gitignoreContent.endsWith('\n') ? '\n' : '') + 
                              '# Atlas MCP marker file\n.atlas\n';
          await fs.writeFile(gitignorePath, gitignoreContent, 'utf-8');
        }
      } catch {
        // Ignore gitignore errors
      }

      return createSuccessResult({
        initialized: true,
        project: {
          id: projectId,
          name: projectName,
          projectRoot,
          marker
        },
        message: `Atlas MCP initialized for project "${projectName}"`
      });

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

/**
 * Check project status
 */
const checkProjectStatusTool = createTool<CheckProjectStatusInput, any>({
  name: 'check_project_status',
  description: 'Check if current directory is a Atlas project',
  category: 'project-management',
  readOnly: true,
  inputSchema: {
    type: 'object',
    properties: {},
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: CheckProjectStatusInput, context: RequestContext) {
    try {
      const projectRoot = process.cwd();
      const markerPath = path.join(projectRoot, '.atlas');

      // Check for marker file
      let marker: ProjectMarker | null = null;
      try {
        const markerContent = await fs.readFile(markerPath, 'utf-8');
        marker = JSON.parse(markerContent);
      } catch {
        return createSuccessResult({
          initialized: false,
          message: 'Not a Atlas project - no .atlas marker file found'
        });
      }

      if (!marker) {
        return createSuccessResult({
          initialized: false,
          message: 'Invalid .atlas marker file'
        });
      }

      // Get project from database
      const projectResult = await context.db.get(
        'SELECT * FROM projects WHERE id = ?',
        [marker.projectId]
      );

      if (!projectResult.data) {
        return createSuccessResult({
          initialized: false,
          marker,
          message: 'Project marker exists but project not found in database'
        });
      }

      // Count related data
      const stats = await Promise.all([
        context.db.get('SELECT COUNT(*) as count FROM agile_stories WHERE project_id = ?', [marker.projectId]),
        context.db.get('SELECT COUNT(*) as count FROM kanban_boards WHERE project_id = ?', [marker.projectId]),
        context.db.get('SELECT COUNT(*) as count FROM documents WHERE project_id = ?', [marker.projectId]),
        context.db.get('SELECT COUNT(*) as count FROM memories WHERE project_id = ?', [marker.projectId])
      ]);

      const dataStats = {
        stories: stats[0].data?.count || 0,
        boards: stats[1].data?.count || 0,
        documents: stats[2].data?.count || 0,
        memories: stats[3].data?.count || 0
      };

      return createSuccessResult({
        initialized: true,
        project: projectResult.data,
        marker,
        dataStats,
        message: `Atlas project "${marker.projectName}" is active`
      });

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

/**
 * Find Atlas project
 */
const findAtlasProjectTool = createTool<FindProjectInput, any>({
  name: 'find_atlas_project',
  description: 'Find the nearest Atlas project in parent directories',
  category: 'project-management',
  readOnly: true,
  inputSchema: {
    type: 'object',
    properties: {},
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: FindProjectInput, context: RequestContext) {
    try {
      const currentDir = process.cwd();
      let searchDir = currentDir;
      let marker: ProjectMarker | null = null;
      let projectRoot: string | null = null;

      // Search upward for .atlas marker file
      while (searchDir !== path.dirname(searchDir)) {
        const markerPath = path.join(searchDir, '.atlas');
        try {
          const markerContent = await fs.readFile(markerPath, 'utf-8');
          marker = JSON.parse(markerContent);
          projectRoot = searchDir;
          break;
        } catch {
          // Continue searching
        }
        searchDir = path.dirname(searchDir);
      }

      if (!marker || !projectRoot) {
        return createSuccessResult({
          found: false,
          searchedFrom: currentDir,
          message: 'No Atlas project found in current or parent directories'
        });
      }

      // Get project from database
      const projectResult = await context.db.get(
        'SELECT * FROM projects WHERE id = ?',
        [marker.projectId]
      );

      return createSuccessResult({
        found: true,
        project: projectResult.data,
        marker,
        projectRoot,
        currentDir,
        isAtRoot: projectRoot === currentDir,
        message: `Found Atlas project "${marker.projectName}" at ${projectRoot}`
      });

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

/**
 * Remove Atlas project
 */
const removeAtlasTool = createTool<RemoveProjectInput, any>({
  name: 'remove_atlas',
  description: 'Remove Atlas MCP from the current project',
  category: 'project-management',
  inputSchema: {
    type: 'object',
    properties: {
      confirm: {
        type: 'boolean',
        default: false,
        description: 'Confirm removal of all Atlas data'
      }
    },
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: RemoveProjectInput, context: RequestContext) {
    try {
      if (!input.confirm) {
        return createSuccessResult({
          requiresConfirmation: true,
          message: 'This will remove Atlas from this project and delete all data. Set confirm=true to proceed.'
        });
      }

      const projectRoot = process.cwd();
      const markerPath = path.join(projectRoot, '.atlas');

      // Read marker file
      let marker: ProjectMarker | null = null;
      try {
        const markerContent = await fs.readFile(markerPath, 'utf-8');
        marker = JSON.parse(markerContent);
      } catch {
        return createSuccessResult({
          message: 'Not a Atlas project - nothing to remove'
        });
      }

      if (!marker) {
        return createSuccessResult({
          message: 'Invalid marker file - nothing to remove'
        });
      }

      // Remove project and all related data from database
      const deleteResult = await context.db.run(
        'DELETE FROM projects WHERE id = ?',
        [marker.projectId]
      );

      if (!deleteResult.success) {
        return createErrorResult({
          code: 'DATABASE_ERROR',
          message: 'Failed to remove project from database',
          details: { error: deleteResult.error },
          category: 'system'
        });
      }

      // Remove marker file
      await fs.unlink(markerPath);

      return createSuccessResult({
        removed: true,
        project: {
          id: marker.projectId,
          name: marker.projectName
        },
        message: `Atlas removed from project "${marker.projectName}"`
      });

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

/**
 * Update project configuration
 */
const updateProjectConfigTool = createTool<UpdateProjectConfigInput, any>({
  name: 'update_project_config',
  description: 'Update project configuration and settings',
  category: 'project-management',
  inputSchema: {
    type: 'object',
    properties: {
      name: {
        type: 'string',
        description: 'New project name',
        maxLength: 200
      },
      description: {
        type: 'string',
        description: 'Project description',
        maxLength: 1000
      },
      settings: {
        type: 'object',
        description: 'Additional project settings',
        additionalProperties: true
      }
    },
    additionalProperties: false
  } as JSONSchema7,

  async execute(input: UpdateProjectConfigInput, context: RequestContext) {
    try {
      const projectRoot = process.cwd();
      const markerPath = path.join(projectRoot, '.atlas');

      // Read marker file
      let marker: ProjectMarker | null = null;
      try {
        const markerContent = await fs.readFile(markerPath, 'utf-8');
        marker = JSON.parse(markerContent);
      } catch {
        return createErrorResult({
          code: 'NOT_FOUND',
          message: 'Not a Atlas project - no .atlas marker file found',
          category: 'validation'
        });
      }

      if (!marker) {
        return createErrorResult({
          code: 'INVALID_DATA',
          message: 'Invalid .atlas marker file',
          category: 'validation'
        });
      }

      // Get current project config
      const projectResult = await context.db.get(
        'SELECT * FROM projects WHERE id = ?',
        [marker.projectId]
      );

      if (!projectResult.data) {
        return createErrorResult({
          code: 'NOT_FOUND',
          message: 'Project not found in database',
          category: 'validation'
        });
      }

      const currentConfig = JSON.parse(projectResult.data.config || '{}');
      const now = Date.now();

      // Build update fields
      const updates: any = {
        updated_at: now
      };

      if (input.name) {
        updates.name = input.name;
        // Update marker file as well
        marker.projectName = input.name;
        await fs.writeFile(markerPath, JSON.stringify(marker, null, 2), 'utf-8');
      }

      if (input.description) {
        updates.description = input.description;
      }

      if (input.settings) {
        const newConfig = {
          ...currentConfig,
          ...input.settings
        };
        updates.config = JSON.stringify(newConfig);
      }

      // Update database
      const updateColumns = Object.keys(updates);
      const updatePlaceholders = updateColumns.map(() => '?').join(', ');
      const updateSet = updateColumns.map(col => `${col} = ?`).join(', ');
      
      const updateResult = await context.db.run(
        `UPDATE projects SET ${updateSet} WHERE id = ?`,
        [...Object.values(updates), marker.projectId]
      );

      if (!updateResult.success) {
        return createErrorResult({
          code: 'DATABASE_ERROR',
          message: 'Failed to update project configuration',
          details: { error: updateResult.error },
          category: 'system'
        });
      }

      // Get updated project
      const updatedProjectResult = await context.db.get(
        'SELECT * FROM projects WHERE id = ?',
        [marker.projectId]
      );

      return createSuccessResult({
        updated: true,
        project: updatedProjectResult.data,
        marker,
        message: `Project configuration updated successfully`
      });

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

/**
 * Setup project management tools
 */
export async function setupProjectManagementTools(): Promise<ToolRegistration> {
  return {
    module: 'project-management',
    tools: [
      initAtlasTool,
      checkProjectStatusTool,
      findAtlasProjectTool,
      removeAtlasTool,
      updateProjectConfigTool
    ]
  };
}