/**
 * summary and tree commands
 *
 * - summary: Get project summary with auto-detected info
 * - tree: Get indexed file tree with optional stats
 */

import { existsSync, readFileSync, writeFileSync } from 'fs';
import { join, basename } from 'path';
import { INDEX_DIR } from '../constants.js';
import { createQueries, type AiDexDatabase } from '../db/index.js';
import { validateIndex, noIndexError, withProjectDb } from './shared.js';

// ============================================================
// Types - Summary
// ============================================================

export interface SummaryParams {
    path: string;           // Project path
}

export interface SummaryResult {
    success: boolean;
    name: string;
    content: string;        // Markdown content of summary.md (if exists)
    autoGenerated: {
        entryPoints: string[];
        mainTypes: string[];
        fileCount: number;
        languages: string[];
    };
    error?: string;
}

// ============================================================
// Types - Tree
// ============================================================

export interface TreeParams {
    path: string;           // Project path
    subpath?: string;       // Subdirectory to list (default: root)
    depth?: number;         // Max depth (default: unlimited)
    includeStats?: boolean; // Include item/method counts per file
}

export interface TreeEntry {
    path: string;
    type: 'file' | 'directory';
    itemCount?: number;
    methodCount?: number;
    typeCount?: number;
}

export interface TreeResult {
    success: boolean;
    root: string;
    entries: TreeEntry[];
    totalFiles: number;
    error?: string;
}

// ============================================================
// Summary implementation
// ============================================================

export function summary(params: SummaryParams): SummaryResult {
    const { path: projectPath } = params;

    return withProjectDb(
        projectPath, true,
        (error) => ({ success: false, name: '', content: '', autoGenerated: { entryPoints: [], mainTypes: [], fileCount: 0, languages: [] }, error }),
        (db, queries) => {
            try {
                const projectName = db.getMetadata('project_name') ?? basename(projectPath);

                // Read summary.md if exists
                const indexDir = join(projectPath, INDEX_DIR);
                const summaryPath = join(indexDir, 'summary.md');
                let content = '';
                if (existsSync(summaryPath)) {
                    content = readFileSync(summaryPath, 'utf-8');
                }

                // Auto-detect entry points
                const entryPoints = detectEntryPoints(queries);

                // Get main types (most referenced)
                const mainTypes = getMainTypes(queries, db);

                // Get file count
                const stats = db.getStats();

                // Detect languages from file extensions
                const languages = detectLanguages(queries);

                return {
                    success: true,
                    name: projectName,
                    content,
                    autoGenerated: {
                        entryPoints,
                        mainTypes,
                        fileCount: stats.files,
                        languages,
                    },
                };
            } catch (err) {
                return {
                    success: false,
                    name: '',
                    content: '',
                    autoGenerated: {
                        entryPoints: [],
                        mainTypes: [],
                        fileCount: 0,
                        languages: [],
                    },
                    error: err instanceof Error ? err.message : String(err),
                };
            }
        }
    );
}

/**
 * Detect entry points based on common patterns
 */
function detectEntryPoints(queries: ReturnType<typeof createQueries>): string[] {
    const files = queries.getAllFiles();
    const entryPatterns = [
        /^(program|main|index|app|application)\.(cs|ts|js|py|rs)$/i,
        /^src\/(program|main|index|app)\.(cs|ts|js|py|rs)$/i,
        /^src\/(main|lib)\.(rs)$/i,
    ];

    const entryPoints: string[] = [];

    for (const file of files) {
        const fileName = file.path.replace(/\\/g, '/');
        for (const pattern of entryPatterns) {
            if (pattern.test(fileName) || pattern.test(basename(fileName))) {
                entryPoints.push(file.path);
                break;
            }
        }
    }

    return entryPoints;
}

/**
 * Get main types (classes/interfaces with most methods)
 * Uses bulk SQL queries instead of N+1 per-file queries.
 * Methods are counted between a type's line_number and the next type's line_number in the same file.
 */
function getMainTypes(queries: ReturnType<typeof createQueries>, db: AiDexDatabase): string[] {
    const rawDb = db.getDb();

    // Load ALL types and methods in two bulk queries
    const allTypes = rawDb.prepare(
        'SELECT t.file_id, t.name, t.line_number, f.path FROM types t JOIN files f ON t.file_id = f.id ORDER BY t.file_id, t.line_number'
    ).all() as Array<{ file_id: number; name: string; line_number: number; path: string }>;

    const allMethods = rawDb.prepare(
        'SELECT file_id, line_number FROM methods ORDER BY file_id, line_number'
    ).all() as Array<{ file_id: number; line_number: number }>;

    // Group methods by file_id for fast lookup
    const methodsByFile = new Map<number, number[]>();
    for (const m of allMethods) {
        let arr = methodsByFile.get(m.file_id);
        if (!arr) {
            arr = [];
            methodsByFile.set(m.file_id, arr);
        }
        arr.push(m.line_number);
    }

    // Count methods per type: only count between this type's start and the next type's start
    const typeMethodCounts: Array<{ name: string; file: string; methodCount: number }> = [];

    for (let i = 0; i < allTypes.length; i++) {
        const type = allTypes[i];
        const nextType = (i + 1 < allTypes.length && allTypes[i + 1].file_id === type.file_id)
            ? allTypes[i + 1]
            : null;

        const fileMethods = methodsByFile.get(type.file_id);
        if (!fileMethods) {
            typeMethodCounts.push({ name: type.name, file: type.path, methodCount: 0 });
            continue;
        }

        const lowerBound = type.line_number;
        const upperBound = nextType ? nextType.line_number : Infinity;

        const methodCount = fileMethods.filter(ln => ln > lowerBound && ln < upperBound).length;
        typeMethodCounts.push({
            name: type.name,
            file: type.path,
            methodCount,
        });
    }

    // Sort by method count and return top 5
    typeMethodCounts.sort((a, b) => b.methodCount - a.methodCount);
    return typeMethodCounts.slice(0, 5).map(t => `${t.name} (${t.file})`);
}

/**
 * Detect languages from indexed file extensions
 */
function detectLanguages(queries: ReturnType<typeof createQueries>): string[] {
    const files = queries.getAllFiles();
    const extensionMap: Record<string, string> = {
        '.cs': 'C#',
        '.ts': 'TypeScript',
        '.tsx': 'TypeScript',
        '.js': 'JavaScript',
        '.jsx': 'JavaScript',
        '.mjs': 'JavaScript',
        '.cjs': 'JavaScript',
        '.rs': 'Rust',
        '.py': 'Python',
        '.pyw': 'Python',
        '.c': 'C',
        '.h': 'C/C++',
        '.cpp': 'C++',
        '.cc': 'C++',
        '.cxx': 'C++',
        '.hpp': 'C++',
        '.hxx': 'C++',
        '.java': 'Java',
        '.go': 'Go',
        '.php': 'PHP',
        '.rb': 'Ruby',
        '.tf': 'HCL/Terraform',
        '.tfvars': 'HCL/Terraform',
        '.hcl': 'HCL/Terraform',
    };

    const languages = new Set<string>();
    for (const file of files) {
        const ext = file.path.substring(file.path.lastIndexOf('.')).toLowerCase();
        if (extensionMap[ext]) {
            languages.add(extensionMap[ext]);
        }
    }

    return [...languages].sort();
}

// ============================================================
// Tree implementation
// ============================================================

export function tree(params: TreeParams): TreeResult {
    const { path: projectPath, subpath, depth, includeStats } = params;

    return withProjectDb(
        projectPath, true,
        (error) => ({ success: false, root: '', entries: [], totalFiles: 0, error }),
        (db, queries) => {
            try {
                const files = queries.getAllFiles();
                const normalizedSubpath = subpath?.replace(/\\/g, '/').replace(/^\/|\/$/g, '') ?? '';

                // Filter files by subpath
                let filteredFiles = files;
                if (normalizedSubpath) {
                    filteredFiles = files.filter(f => {
                        const normalizedPath = f.path.replace(/\\/g, '/');
                        return normalizedPath.startsWith(normalizedSubpath + '/') ||
                               normalizedPath === normalizedSubpath;
                    });
                }

                // Pre-load stats in bulk if needed (avoids N+1 queries)
                let statsMap: Map<number, { itemCount: number; methodCount: number; typeCount: number }> | null = null;
                if (includeStats) {
                    const rawDb = db.getDb();
                    statsMap = new Map();

                    // Bulk load unique item counts per file
                    const itemCounts = rawDb.prepare(
                        'SELECT file_id, COUNT(DISTINCT item_id) as cnt FROM occurrences GROUP BY file_id'
                    ).all() as Array<{ file_id: number; cnt: number }>;
                    for (const row of itemCounts) {
                        statsMap.set(row.file_id, { itemCount: row.cnt, methodCount: 0, typeCount: 0 });
                    }

                    // Bulk load method counts per file
                    const methodCounts = rawDb.prepare(
                        'SELECT file_id, COUNT(*) as cnt FROM methods GROUP BY file_id'
                    ).all() as Array<{ file_id: number; cnt: number }>;
                    for (const row of methodCounts) {
                        const entry = statsMap.get(row.file_id);
                        if (entry) {
                            entry.methodCount = row.cnt;
                        } else {
                            statsMap.set(row.file_id, { itemCount: 0, methodCount: row.cnt, typeCount: 0 });
                        }
                    }

                    // Bulk load type counts per file
                    const typeCounts = rawDb.prepare(
                        'SELECT file_id, COUNT(*) as cnt FROM types GROUP BY file_id'
                    ).all() as Array<{ file_id: number; cnt: number }>;
                    for (const row of typeCounts) {
                        const entry = statsMap.get(row.file_id);
                        if (entry) {
                            entry.typeCount = row.cnt;
                        } else {
                            statsMap.set(row.file_id, { itemCount: 0, methodCount: 0, typeCount: row.cnt });
                        }
                    }
                }

                // Build tree structure
                const directories = new Set<string>();
                const entries: TreeEntry[] = [];

                for (const file of filteredFiles) {
                    const normalizedPath = file.path.replace(/\\/g, '/');
                    const relativePath = normalizedSubpath
                        ? normalizedPath.substring(normalizedSubpath.length + 1)
                        : normalizedPath;

                    // Check depth
                    const pathDepth = relativePath.split('/').length;
                    if (depth !== undefined && pathDepth > depth) {
                        // Just add parent directories up to depth
                        const parts = relativePath.split('/');
                        for (let i = 0; i < Math.min(depth, parts.length - 1); i++) {
                            const dirPath = parts.slice(0, i + 1).join('/');
                            directories.add(dirPath);
                        }
                        continue;
                    }

                    // Add parent directories
                    const parts = relativePath.split('/');
                    for (let i = 0; i < parts.length - 1; i++) {
                        const dirPath = parts.slice(0, i + 1).join('/');
                        directories.add(dirPath);
                    }

                    // Add file entry
                    const entry: TreeEntry = {
                        path: relativePath,
                        type: 'file',
                    };

                    if (statsMap) {
                        const fileStats = statsMap.get(file.id);
                        entry.itemCount = fileStats?.itemCount ?? 0;
                        entry.methodCount = fileStats?.methodCount ?? 0;
                        entry.typeCount = fileStats?.typeCount ?? 0;
                    }

                    entries.push(entry);
                }

                // Add directory entries
                for (const dir of directories) {
                    entries.push({
                        path: dir,
                        type: 'directory',
                    });
                }

                // Sort: directories first, then alphabetically
                entries.sort((a, b) => {
                    if (a.type !== b.type) {
                        return a.type === 'directory' ? -1 : 1;
                    }
                    return a.path.localeCompare(b.path);
                });

                return {
                    success: true,
                    root: normalizedSubpath || '.',
                    entries,
                    totalFiles: filteredFiles.length,
                };
            } catch (err) {
                return {
                    success: false,
                    root: '',
                    entries: [],
                    totalFiles: 0,
                    error: err instanceof Error ? err.message : String(err),
                };
            }
        }
    );
}

// ============================================================
// Describe - Add content to summary.md
// ============================================================

export interface DescribeParams {
    path: string;
    section: 'purpose' | 'architecture' | 'concepts' | 'patterns' | 'notes';
    content: string;
    replace?: boolean;      // Replace existing section? Default: append
}

export interface DescribeResult {
    success: boolean;
    section: string;
    error?: string;
}

export function describe(params: DescribeParams): DescribeResult {
    const { path: projectPath, section, content, replace = false } = params;

    // Validate project path
    if (!validateIndex(projectPath)) {
        return {
            success: false,
            section,
            error: noIndexError(projectPath),
        };
    }

    const indexDir = join(projectPath, INDEX_DIR);
    const summaryPath = join(indexDir, 'summary.md');

    try {
        // Read existing summary or create new
        let summaryContent = '';
        if (existsSync(summaryPath)) {
            summaryContent = readFileSync(summaryPath, 'utf-8');
        }

        // Section headers
        const sectionHeaders: Record<string, string> = {
            purpose: '## Purpose',
            architecture: '## Architecture',
            concepts: '## Key Concepts',
            patterns: '## Patterns',
            notes: '## Notes',
        };

        const header = sectionHeaders[section];
        const sectionRegex = new RegExp(`^${header}\\n([\\s\\S]*?)(?=^## |$)`, 'm');

        if (replace) {
            // Replace entire section
            if (sectionRegex.test(summaryContent)) {
                summaryContent = summaryContent.replace(sectionRegex, `${header}\n${content}\n\n`);
            } else {
                // Add new section at end
                summaryContent = summaryContent.trimEnd() + `\n\n${header}\n${content}\n`;
            }
        } else {
            // Append to section
            const match = summaryContent.match(sectionRegex);
            if (match) {
                const existingContent = match[1].trimEnd();
                const newContent = existingContent ? `${existingContent}\n${content}` : content;
                summaryContent = summaryContent.replace(sectionRegex, `${header}\n${newContent}\n\n`);
            } else {
                // Add new section at end
                summaryContent = summaryContent.trimEnd() + `\n\n${header}\n${content}\n`;
            }
        }

        // Write back
        writeFileSync(summaryPath, summaryContent.trimStart());

        return {
            success: true,
            section,
        };
    } catch (err) {
        return {
            success: false,
            section,
            error: err instanceof Error ? err.message : String(err),
        };
    }
}
