import * as fs from 'fs';
import * as path from 'path';
import { MarkdownFileWriter } from './markdownFileWriter';
import {
    BugRecord,
    ChronicleRecordEntry,
    ChronicleWriteResult,
    DecisionRecord,
    DevelopmentLog,
    DiscussionRecord,
    PlanningDocument,
    ProjectProfile,
    RetrospectiveRecord
} from './types';
import {
    renderBugRecord,
    renderDecisionRecord,
    renderDevelopmentLog,
    renderDiscussionRecord,
    renderPlanningDocument,
    renderProjectProfile,
    renderProjectReadme,
    renderRetrospective,
    renderTimelineSeed
} from './templates';

export * from './types';
export * from './guardPrompt';

const sectionDirs = ['planning', 'discussions', 'decisions', 'development', 'bugs', 'retrospectives'];

export class ProjectChronicleManager {
    private readonly writer = new MarkdownFileWriter();

    public ensureProject(profile: ProjectProfile): void {
        if (!profile.recordRoot || !profile.recordRoot.trim()) {
            throw new Error('Record root is required before writing chronicle documents.');
        }

        this.writer.ensureDir(profile.recordRoot);
        for (const dir of sectionDirs) {
            this.writer.ensureDir(path.join(profile.recordRoot, dir));
        }

        this.writeSeedFile(path.join(profile.recordRoot, 'README.md'), renderProjectReadme(profile));
        this.writeSeedFile(path.join(profile.recordRoot, 'project-profile.md'), renderProjectProfile(profile));
        this.writeSeedFile(path.join(profile.recordRoot, 'timeline.md'), renderTimelineSeed(profile));
        this.writeProjectConfig(profile);
    }

    public writePlanning(profile: ProjectProfile, doc: PlanningDocument): ChronicleWriteResult {
        this.ensureProject(profile);
        const date = this.datePart(doc.createdAt);
        const fileName = `${date}_${this.slug(doc.featureName)}.md`;
        return this.write(profile, 'planning', fileName, renderPlanningDocument(doc));
    }

    public writeDiscussion(profile: ProjectProfile, record: DiscussionRecord): ChronicleWriteResult {
        this.ensureProject(profile);
        const date = this.datePart(record.createdAt);
        const fileName = `${date}_${this.slug(record.title)}.md`;
        return this.write(profile, 'discussions', fileName, renderDiscussionRecord(record));
    }

    public writeDecision(profile: ProjectProfile, record: DecisionRecord, adrNumber: number): ChronicleWriteResult {
        this.ensureProject(profile);
        const padded = String(adrNumber).padStart(4, '0');
        const fileName = `ADR-${padded}-${this.slug(record.title)}.md`;
        return this.write(profile, 'decisions', fileName, renderDecisionRecord(record));
    }

    public writeDevelopmentLog(profile: ProjectProfile, log: DevelopmentLog): ChronicleWriteResult {
        this.ensureProject(profile);
        const date = this.datePart(log.createdAt);
        const fileName = `${date}_${this.slug(log.featureName)}_implementation.md`;
        return this.write(profile, 'development', fileName, renderDevelopmentLog(log));
    }

    public writeBug(profile: ProjectProfile, record: BugRecord, bugNumber: number): ChronicleWriteResult {
        this.ensureProject(profile);
        const padded = String(bugNumber).padStart(4, '0');
        const fileName = `BUG-${padded}-${this.slug(record.title)}.md`;
        return this.write(profile, 'bugs', fileName, renderBugRecord(record));
    }

    public writeRetrospective(profile: ProjectProfile, record: RetrospectiveRecord): ChronicleWriteResult {
        this.ensureProject(profile);
        const date = this.datePart(record.createdAt);
        const fileName = `${date}_${this.slug(record.title)}.md`;
        return this.write(profile, 'retrospectives', fileName, renderRetrospective(record));
    }

    public appendTimeline(profile: ProjectProfile, lines: string[], createdAt: string = new Date().toISOString()): void {
        this.ensureProject(profile);
        const timelinePath = path.join(profile.recordRoot, 'timeline.md');
        const markdown = [
            '',
            `## ${this.datePart(createdAt)}`,
            ...lines.map(line => `- ${line}`)
        ].join('\n');
        this.writer.appendMarkdown(timelinePath, markdown);
    }

    public nextAdrNumber(profile: ProjectProfile): number {
        return this.nextNumber(path.join(profile.recordRoot, 'decisions'), /^ADR-(\d+)/);
    }

    public nextBugNumber(profile: ProjectProfile): number {
        return this.nextNumber(path.join(profile.recordRoot, 'bugs'), /^BUG-(\d+)/);
    }

    public listRecords(profile: ProjectProfile): ChronicleRecordEntry[] {
        this.ensureProject(profile);
        const records: ChronicleRecordEntry[] = [];

        for (const section of sectionDirs) {
            const sectionPath = path.join(profile.recordRoot, section);
            if (!fs.existsSync(sectionPath)) continue;

            for (const fileName of fs.readdirSync(sectionPath)) {
                if (!fileName.endsWith('.md')) continue;
                const filePath = path.join(sectionPath, fileName);
                const stat = fs.statSync(filePath);
                records.push({
                    section,
                    fileName,
                    filePath,
                    relativePath: path.relative(profile.recordRoot, filePath),
                    updatedAt: stat.mtimeMs
                });
            }
        }

        return records.sort((a, b) => b.updatedAt - a.updatedAt);
    }

    private write(profile: ProjectProfile, section: string, fileName: string, markdown: string): ChronicleWriteResult {
        const filePath = this.writer.writeMarkdown(path.join(profile.recordRoot, section, fileName), markdown);
        return {
            filePath,
            relativePath: path.relative(profile.recordRoot, filePath)
        };
    }

    private writeSeedFile(filePath: string, content: string): void {
        if (!fs.existsSync(filePath)) {
            this.writer.writeMarkdown(filePath, content);
        }
    }

    private writeProjectConfig(profile: ProjectProfile): void {
        const configPath = path.join(profile.recordRoot, 'chronicle.config.json');
        const config = {
            projectId: profile.projectId,
            projectName: profile.projectName,
            projectRoot: profile.projectRoot || '',
            recordRoot: profile.recordRoot,
            description: profile.description || '',
            corePurpose: profile.corePurpose || '',
            detailLevel: profile.detailLevel,
            createdAt: profile.createdAt,
            updatedAt: new Date().toISOString()
        };
        fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, 'utf8');
    }

    private nextNumber(dir: string, pattern: RegExp): number {
        if (!fs.existsSync(dir)) return 1;
        const max = fs.readdirSync(dir).reduce((current, fileName) => {
            const match = fileName.match(pattern);
            if (!match) return current;
            return Math.max(current, Number(match[1]) || 0);
        }, 0);
        return max + 1;
    }

    private slug(value: string): string {
        const slug = value
            .toLowerCase()
            .replace(/[^a-z0-9가-힣]+/g, '-')
            .replace(/^-|-$/g, '')
            .slice(0, 60);
        return slug || 'record';
    }

    private datePart(iso?: string): string {
        return (iso || new Date().toISOString()).slice(0, 10);
    }
}
