import { NextRequest, NextResponse } from "next/server";
import { promises as fs } from "fs";
import path from "path";
import {
  parseManicTimeFile,
  serializeToManicTime,
  computeSyncPlan,
  generateBackupFilename,
  validateHierarchy,
  manicTimeTagToCreateData,
} from "@/modules/tag-manager/services/manictime-service";
import type { Tag } from "@/core/tagging/types";
import { prisma } from "@/core/storage/prisma";
import type { Prisma } from "@prisma/client";
const NAMESPACE = "tags";

/**
 * Get the ManicTime Tags.txt file path from environment.
 * Default: /mnt/manictime/Tags.txt (Docker SMB mount)
 */
function getTagsFilePath(): string {
  return process.env.MANICTIME_TAGS_PATH ?? "/mnt/manictime/Tags.txt";
}

/**
 * Load all tags from the database (same namespace as TagService).
 */
async function loadArchiToolsTags(): Promise<Tag[]> {
  const records = await prisma.keyValueStore.findMany({
    where: { namespace: NAMESPACE },
  });
  return records.map((r) => r.value as unknown as Tag);
}

/**
 * GET /api/manictime
 * Read and parse the ManicTime Tags.txt file.
 * Returns parsed tags + sync plan against ArchiTools.
 */
export async function GET() {
  const filePath = getTagsFilePath();

  try {
    await fs.access(filePath);
  } catch {
    return NextResponse.json(
      {
        error: "file_not_found",
        message: `Fișierul Tags.txt nu a fost găsit la: ${filePath}. Verificați montarea SMB și variabila MANICTIME_TAGS_PATH.`,
        filePath,
      },
      { status: 404 },
    );
  }

  try {
    const content = await fs.readFile(filePath, "utf-8");
    const parsed = parseManicTimeFile(content);
    const archiToolsTags = await loadArchiToolsTags();
    const syncPlan = computeSyncPlan(parsed.tags, archiToolsTags);

    return NextResponse.json({
      filePath,
      lastModified: (await fs.stat(filePath)).mtime.toISOString(),
      totalLines: parsed.tags.length,
      groups: parsed.groups.length,
      syncPlan: {
        addToArchiTools: syncPlan.addedToArchiTools.length,
        addToManicTime: syncPlan.addedToManicTime.length,
        unchanged: syncPlan.unchanged,
        conflicts: syncPlan.conflicts,
      },
      manicTimeTags: parsed.tags,
      archiToolsCount: archiToolsTags.length,
    });
  } catch (error) {
    return NextResponse.json(
      {
        error: "read_error",
        message: `Eroare la citirea fișierului: ${error instanceof Error ? error.message : "necunoscută"}`,
      },
      { status: 500 },
    );
  }
}

/**
 * POST /api/manictime
 * Perform bidirectional sync.
 *
 * Body: { direction: 'pull' | 'push' | 'both' }
 * - pull: Import new tags from ManicTime → ArchiTools
 * - push: Export ArchiTools tags → ManicTime Tags.txt (with backup)
 * - both: Pull then push
 */
export async function POST(request: NextRequest) {
  const filePath = getTagsFilePath();

  let body: { direction?: string } = {};
  try {
    body = (await request.json()) as { direction?: string };
  } catch {
    return NextResponse.json({ error: "invalid_json" }, { status: 400 });
  }

  const direction = body.direction ?? "both";
  if (!["pull", "push", "both"].includes(direction)) {
    return NextResponse.json(
      {
        error: "invalid_direction",
        message: "Direction must be pull, push, or both",
      },
      { status: 400 },
    );
  }

  try {
    const archiToolsTags = await loadArchiToolsTags();
    let pulled = 0;
    let pushed = false;

    // ── Pull: ManicTime → ArchiTools ──
    if (direction === "pull" || direction === "both") {
      let content: string;
      try {
        content = await fs.readFile(filePath, "utf-8");
      } catch {
        return NextResponse.json(
          {
            error: "file_not_found",
            message: `Fișierul Tags.txt nu a fost găsit la: ${filePath}`,
          },
          { status: 404 },
        );
      }

      const parsed = parseManicTimeFile(content);
      const syncPlan = computeSyncPlan(parsed.tags, archiToolsTags);

      // Import new tags into ArchiTools DB
      const existingKeys = new Set(
        archiToolsTags.map((t) => `${t.category}::${t.label.toLowerCase()}`),
      );

      for (const mt of syncPlan.addedToArchiTools) {
        const data = manicTimeTagToCreateData(mt);
        if (!data) continue;

        const key = `${data.category}::${data.label.toLowerCase()}`;
        if (existingKeys.has(key)) continue;

        const id = crypto.randomUUID();
        const tag: Tag = {
          ...data,
          id,
          createdAt: new Date().toISOString(),
        };

        const tagJson = JSON.parse(
          JSON.stringify(tag),
        ) as Prisma.InputJsonValue;

        await prisma.keyValueStore.upsert({
          where: { namespace_key: { namespace: NAMESPACE, key: id } },
          update: { value: tagJson },
          create: {
            namespace: NAMESPACE,
            key: id,
            value: tagJson,
          },
        });

        existingKeys.add(key);
        pulled++;
      }
    }

    // ── Push: ArchiTools → ManicTime ──
    if (direction === "push" || direction === "both") {
      // Re-load to include any newly pulled tags
      const allTags = await loadArchiToolsTags();

      // Validate hierarchy before writing
      const warnings = validateHierarchy(allTags);

      // Create backup if file exists
      try {
        await fs.access(filePath);
        const backupPath = generateBackupFilename(filePath);
        await fs.copyFile(filePath, backupPath);
      } catch {
        // File doesn't exist yet, no backup needed
      }

      // Ensure directory exists
      const dir = path.dirname(filePath);
      await fs.mkdir(dir, { recursive: true });

      // Write new content
      const newContent = serializeToManicTime(allTags);
      await fs.writeFile(filePath, newContent, "utf-8");
      pushed = true;

      return NextResponse.json({
        success: true,
        direction,
        pulled,
        pushed,
        totalTags: allTags.length,
        warnings,
        timestamp: new Date().toISOString(),
      });
    }

    return NextResponse.json({
      success: true,
      direction,
      pulled,
      pushed,
      totalTags: archiToolsTags.length + pulled,
      warnings: [],
      timestamp: new Date().toISOString(),
    });
  } catch (error) {
    return NextResponse.json(
      {
        error: "sync_error",
        message: `Eroare la sincronizare: ${error instanceof Error ? error.message : "necunoscută"}`,
      },
      { status: 500 },
    );
  }
}
