import fs from 'fs/promises';
import path from 'path';
import { v4 as uuidv4 } from 'uuid';
import { getContext } from '../context.js';

const DRAFTS_ROOT = path.resolve(process.cwd(), 'drafts');
const ACTIVE_DRAFT_ID = 'active';

interface DraftFile {
  filename: string;
  mimeType: string;
}

interface DraftData {
  id: string; // Will always be 'active' for the single-draft logic
  templateId: string;
  createdAt: string;
  fields: Record<string, any>;
  files: DraftFile[]; 
}

class DraftManager {
  private async getUserDir(): Promise<string> {
    const { claims, sessionId } = getContext();
    const userId = String(sessionId || claims?.sub || claims?.id || 'guest');
    // Sanitize user ID
    const safeUserId = userId.replace(/[^a-zA-Z0-9-]/g, '');
    const dir = path.join(DRAFTS_ROOT, safeUserId);
    await fs.mkdir(dir, { recursive: true });
    return dir;
  }

  private async getDraftDir(): Promise<string> {
    const userDir = await this.getUserDir();
    return path.join(userDir, ACTIVE_DRAFT_ID);
  }

  async createDraft(templateId: string, initialFields: Record<string, any> = {}): Promise<DraftData> {
    const draftDir = await this.getDraftDir();
    
    let existing: DraftData | null = null;
    try {
      existing = await this.getDraft();
    } catch (e) {
      // No existing draft
    }

    // If template changed, we might want to clear old fields or at least update templateId
    const draft: DraftData = {
      id: ACTIVE_DRAFT_ID,
      templateId,
      createdAt: existing ? existing.createdAt : new Date().toISOString(),
      fields: existing && existing.templateId === templateId 
        ? { ...existing.fields, ...initialFields } 
        : initialFields,
      files: existing && existing.templateId === templateId ? existing.files : []
    };

    if (existing && existing.templateId !== templateId) {
      // Clear files if template changed because required files might be different
      await fs.rm(draftDir, { recursive: true, force: true });
    }

    await fs.mkdir(draftDir, { recursive: true });
    await this.saveDraftJson(draft);
    return draft;
  }

  async listDrafts(): Promise<DraftData[]> {
    try {
      const draft = await this.getDraft();
      return [draft];
    } catch (e) {
      return [];
    }
  }

  async getDraft(): Promise<DraftData> {
    const draftDir = await this.getDraftDir();
    const jsonPath = path.join(draftDir, 'data.json');
    try {
      const content = await fs.readFile(jsonPath, 'utf-8');
      return JSON.parse(content);
    } catch (error: any) {
      if (error.code === 'ENOENT') {
        throw new Error(`No active draft found for this user.`);
      }
      throw error;
    }
  }

  async updateDraftFields(fields: Record<string, any>): Promise<DraftData> {
    const draft = await this.getDraft();
    
    // Merge customFields if they exist in both
    if (fields.customFields && draft.fields.customFields) {
      draft.fields.customFields = { ...draft.fields.customFields, ...fields.customFields };
      const { customFields, ...otherFields } = fields;
      draft.fields = { ...draft.fields, ...otherFields };
    } else {
      draft.fields = { ...draft.fields, ...fields };
    }

    await this.saveDraftJson(draft);
    return draft;
  }

  async addFile(filename: string, base64Content: string, mimeType: string = 'application/octet-stream'): Promise<DraftData> {
    const draft = await this.getDraft();
    const draftDir = await this.getDraftDir();
    const safeFilename = path.basename(filename);
    const filePath = path.join(draftDir, safeFilename);
    
    const buffer = Buffer.from(base64Content, 'base64');
    await fs.writeFile(filePath, buffer);

    const existingFile = draft.files.find(f => f.filename === safeFilename);
    if (!existingFile) {
      draft.files.push({ filename: safeFilename, mimeType });
      await this.saveDraftJson(draft);
    } else if (existingFile.mimeType !== mimeType) {
      existingFile.mimeType = mimeType;
      await this.saveDraftJson(draft);
    }
    
    return draft;
  }

  async getFilePath(filename: string): Promise<string> {
    const draftDir = await this.getDraftDir();
    return path.join(draftDir, filename);
  }

  async deleteDraft(): Promise<void> {
    const draftDir = await this.getDraftDir();
    await fs.rm(draftDir, { recursive: true, force: true });
  }

  async deleteAllDrafts(): Promise<void> {
    await this.deleteDraft();
  }

  private async saveDraftJson(data: DraftData) {
    const draftDir = await this.getDraftDir();
    await fs.writeFile(path.join(draftDir, 'data.json'), JSON.stringify(data, null, 2));
  }
}

export const draftManager = new DraftManager();
