/**
 * ManicTime Tags.txt Parser & Serializer
 *
 * The Tags.txt format is a plain-text file with one tag per line.
 * Blank lines separate groups (categories). The parser maps groups
 * to ArchiTools tag categories based on pattern recognition:
 *
 * - Lines matching `\d+ .+` or `L\d+ .+` → project tags
 * - Known phase names (CU, Schita, DTAC, etc.) → phase tags
 * - Known activity names (Ofertare, Documentare, etc.) → activity tags
 * - Other → custom tags
 */

import type { Tag, TagCategory } from "@/core/tagging/types";
import type { CompanyId } from "@/core/auth/types";

// ── Known phase labels (case-insensitive match) ──

const KNOWN_PHASES = new Set([
  "cu",
  "schita",
  "avize",
  "pud",
  "ao",
  "puz",
  "pug",
  "dtad",
  "dtac",
  "pt",
  "detalii de executie",
  "studii de fundamentare",
  "regulament",
  "parte desenata",
  "parte scrisa",
  "consultanta client",
  "macheta",
  "consultanta receptie",
  "redactare",
  "depunere",
  "ridicare",
  "verificare proiect",
  "vizita santier",
]);

// ── Known activity labels ──

const KNOWN_ACTIVITIES = new Set([
  "ofertare",
  "configurari",
  "organizare initiala",
  "pregatire portofoliu",
  "website",
  "documentare",
  "design grafic",
  "design interior",
  "design exterior",
  "releveu",
  "reclama",
  "master matdr",
  "pauza de masa",
  "timp personal",
  "concediu",
  "compensare overtime",
]);

// ── Company identifiers that appear as group headers ──

const COMPANY_HEADERS = new Set([
  "beletage",
  "urban switch",
  "studii de teren",
]);

/** Map group header text → CompanyId */
const HEADER_TO_COMPANY: Record<string, CompanyId> = {
  beletage: "beletage",
  "urban switch": "urban-switch",
  "studii de teren": "studii-de-teren",
};

// ── Project line pattern: optional letter prefix + digits + space + name ──

const PROJECT_LINE_RE = /^(\w?\d+)\s+(.+)$/;
// Special pattern for "176 - 2025 - ReAC Ansamblu rezi Bibescu" style
const PROJECT_LINE_EXTENDED_RE = /^(\d+)\s+-\s+(?:\d{4}\s+-\s+)?(.+)$/;

export interface ManicTimeTag {
  line: string;
  category: TagCategory | "header" | "unknown";
  projectCode?: string;
  companyId?: CompanyId;
  label: string;
}

export interface ParsedManicTimeFile {
  groups: string[][];
  tags: ManicTimeTag[];
  rawContent: string;
}

export interface SyncResult {
  addedToArchiTools: ManicTimeTag[];
  addedToManicTime: Tag[];
  unchanged: number;
  conflicts: string[];
}

/**
 * Parse a ManicTime Tags.txt file into structured data.
 */
export function parseManicTimeFile(content: string): ParsedManicTimeFile {
  const lines = content.split(/\r?\n/);
  const groups: string[][] = [];
  let currentGroup: string[] = [];

  for (const line of lines) {
    if (line.trim() === "") {
      if (currentGroup.length > 0) {
        groups.push(currentGroup);
        currentGroup = [];
      }
    } else {
      currentGroup.push(line.trim());
    }
  }
  if (currentGroup.length > 0) {
    groups.push(currentGroup);
  }

  const tags: ManicTimeTag[] = [];
  let currentCompany: CompanyId | undefined;

  for (const group of groups) {
    // Check if the first line of this group is a company header
    const firstLine = group[0];
    if (firstLine) {
      const headerCompany = HEADER_TO_COMPANY[firstLine.toLowerCase()];
      if (headerCompany) {
        currentCompany = headerCompany;
      }
    }

    for (const line of group) {
      const tag = classifyLine(line);
      // Assign company context to project tags
      if (tag.category === "project" && currentCompany) {
        tag.companyId = currentCompany;
      }
      tags.push(tag);
    }
  }

  return { groups, tags, rawContent: content };
}

/**
 * Classify a single line from Tags.txt.
 */
function classifyLine(line: string): ManicTimeTag {
  const trimmed = line.trim();
  const lower = trimmed.toLowerCase();

  // Company header
  if (COMPANY_HEADERS.has(lower)) {
    return { line: trimmed, category: "header", label: trimmed };
  }

  // Project line (e.g. "000 Farmacie", "L089 PUZ TUSA-BOJAN", "176 - 2025 - ReAC...")
  const projectMatch = trimmed.match(PROJECT_LINE_RE);
  if (projectMatch?.[1] && projectMatch[2]) {
    const num = projectMatch[1];
    const label = projectMatch[2].trim();
    const padded = num.replace(/^[A-Z]/i, "").padStart(3, "0");
    const prefix = num.match(/^[A-Z]/i) ? `B${num.charAt(0)}` : "B";
    return {
      line: trimmed,
      category: "project",
      projectCode: `${prefix}-${padded}`,
      label,
    };
  }
  const extendedMatch = trimmed.match(PROJECT_LINE_EXTENDED_RE);
  if (extendedMatch?.[1] && extendedMatch[2]) {
    const extNum = extendedMatch[1];
    const extLabel = extendedMatch[2].trim();
    return {
      line: trimmed,
      category: "project",
      projectCode: `B-${extNum.padStart(3, "0")}`,
      label: extLabel,
    };
  }

  // Phase
  if (KNOWN_PHASES.has(lower)) {
    return { line: trimmed, category: "phase", label: trimmed };
  }

  // Activity
  if (KNOWN_ACTIVITIES.has(lower)) {
    return { line: trimmed, category: "activity", label: trimmed };
  }

  // Unknown / custom
  return { line: trimmed, category: "unknown", label: trimmed };
}

/**
 * Convert ArchiTools tags back to ManicTime Tags.txt format.
 * Groups: activities, projects (by company), phases, custom.
 */
export function serializeToManicTime(tags: Tag[]): string {
  const lines: string[] = [];

  // ── Group 1: General activities (personal, leave) ──
  const personalActivities = ["Pauza de masa", "Timp personal"];
  const leaveActivities = ["Concediu", "Compensare overtime"];

  const personalFound = tags.filter((t) =>
    personalActivities.some((a) => t.label.toLowerCase() === a.toLowerCase()),
  );
  if (personalFound.length > 0) {
    for (const t of personalFound) lines.push(t.label);
    lines.push("");
  }

  const leaveFound = tags.filter((t) =>
    leaveActivities.some((a) => t.label.toLowerCase() === a.toLowerCase()),
  );
  if (leaveFound.length > 0) {
    for (const t of leaveFound) lines.push(t.label);
    lines.push("");
  }

  // ── Group 2: Company activities ──
  const companyActivities = tags.filter(
    (t) =>
      t.category === "activity" &&
      !personalActivities.some(
        (a) => t.label.toLowerCase() === a.toLowerCase(),
      ) &&
      !leaveActivities.some((a) => t.label.toLowerCase() === a.toLowerCase()),
  );
  if (companyActivities.length > 0) {
    lines.push("Beletage");
    for (const t of companyActivities) lines.push(t.label);
    lines.push("");
  }

  // ── Group 3: Projects by company ──
  const companies: CompanyId[] = [
    "beletage",
    "urban-switch",
    "studii-de-teren",
  ];
  for (const company of companies) {
    const companyProjects = tags
      .filter((t) => t.category === "project" && t.companyId === company)
      .sort((a, b) => (a.projectCode ?? "").localeCompare(b.projectCode ?? ""));

    if (companyProjects.length > 0) {
      for (const t of companyProjects) {
        const code = t.projectCode ?? "";
        // Extract numeric part from project code like "B-000" → "000"
        const numMatch = code.match(/\d+$/);
        const num = numMatch?.[0] ?? "000";
        lines.push(`${num} ${t.label}`);
      }
      lines.push("");
    }
  }

  // ── Group 4: Phases ──
  const phases = tags.filter((t) => t.category === "phase");
  if (phases.length > 0) {
    for (const t of phases) lines.push(t.label);
    lines.push("");
  }

  // ── Group 5: Document types ──
  const docTypes = tags.filter((t) => t.category === "document-type");
  if (docTypes.length > 0) {
    for (const t of docTypes) lines.push(t.label);
    lines.push("");
  }

  // ── Group 6: Custom ──
  const custom = tags.filter((t) => t.category === "custom");
  if (custom.length > 0) {
    for (const t of custom) lines.push(t.label);
    lines.push("");
  }

  return lines.join("\n").trimEnd() + "\n";
}

/**
 * Compare ManicTime file content with ArchiTools tags and produce a sync plan.
 */
export function computeSyncPlan(
  manicTimeTags: ManicTimeTag[],
  archiToolsTags: Tag[],
): SyncResult {
  const result: SyncResult = {
    addedToArchiTools: [],
    addedToManicTime: [],
    unchanged: 0,
    conflicts: [],
  };

  // Build a normalized-label set from ArchiTools
  const archiLabels = new Set(
    archiToolsTags.map((t) => t.label.toLowerCase().trim()),
  );
  const archiCodes = new Set(
    archiToolsTags
      .filter((t) => t.projectCode)
      .map((t) => t.projectCode!.toLowerCase()),
  );

  // Build a normalized-label set from ManicTime
  const manicLabels = new Set(
    manicTimeTags
      .filter((t) => t.category !== "header")
      .map((t) => t.label.toLowerCase().trim()),
  );

  // Tags in ManicTime but not in ArchiTools → add to ArchiTools
  for (const mt of manicTimeTags) {
    if (mt.category === "header" || mt.category === "unknown") continue;

    const labelKey = mt.label.toLowerCase().trim();
    const codeKey = mt.projectCode?.toLowerCase();

    if (!archiLabels.has(labelKey) && !(codeKey && archiCodes.has(codeKey))) {
      result.addedToArchiTools.push(mt);
    } else {
      result.unchanged++;
    }
  }

  // Tags in ArchiTools but not in ManicTime → add to ManicTime
  for (const at of archiToolsTags) {
    const labelKey = at.label.toLowerCase().trim();
    if (!manicLabels.has(labelKey)) {
      result.addedToManicTime.push(at);
    }
  }

  return result;
}

/**
 * Convert a ManicTimeTag to the shape needed by TagService.createTag().
 */
export function manicTimeTagToCreateData(
  mt: ManicTimeTag,
): Omit<Tag, "id" | "createdAt"> | null {
  if (mt.category === "header" || mt.category === "unknown") return null;

  const category = mt.category as TagCategory;
  const base: Omit<Tag, "id" | "createdAt"> = {
    label: mt.label,
    category,
    scope: category === "project" ? "company" : "global",
    color:
      category === "project"
        ? "#22B5AB"
        : category === "phase"
          ? "#3b82f6"
          : "#8b5cf6",
  };

  if (category === "project") {
    base.companyId = mt.companyId ?? "beletage";
    base.projectCode = mt.projectCode;
  }

  return base;
}

/**
 * Generate a backup filename with timestamp.
 */
export function generateBackupFilename(originalPath: string): string {
  const now = new Date();
  const ts = [
    now.getFullYear(),
    String(now.getMonth() + 1).padStart(2, "0"),
    String(now.getDate()).padStart(2, "0"),
    "_",
    String(now.getHours()).padStart(2, "0"),
    String(now.getMinutes()).padStart(2, "0"),
    String(now.getSeconds()).padStart(2, "0"),
  ].join("");

  // Replace .txt with _backup_TIMESTAMP.txt
  const dotIndex = originalPath.lastIndexOf(".");
  if (dotIndex >= 0) {
    return `${originalPath.substring(0, dotIndex)}_backup_${ts}${originalPath.substring(dotIndex)}`;
  }
  return `${originalPath}_backup_${ts}`;
}

/**
 * Validate that tags respect ManicTime's hierarchical format.
 * Returns a list of validation warnings (empty = valid).
 */
export function validateHierarchy(tags: Tag[]): string[] {
  const warnings: string[] = [];

  // Projects must have a project code
  const projectsWithoutCode = tags.filter(
    (t) => t.category === "project" && !t.projectCode,
  );
  if (projectsWithoutCode.length > 0) {
    warnings.push(
      `${projectsWithoutCode.length} proiecte fără cod (ex: B-001). ManicTime necesită un format numeric.`,
    );
  }

  // Projects should have a company
  const projectsWithoutCompany = tags.filter(
    (t) => t.category === "project" && !t.companyId,
  );
  if (projectsWithoutCompany.length > 0) {
    warnings.push(
      `${projectsWithoutCompany.length} proiecte fără companie asociată.`,
    );
  }

  // Check for duplicate labels within same category
  const seen = new Map<string, string>();
  for (const tag of tags) {
    const key = `${tag.category}::${tag.label.toLowerCase()}`;
    const existing = seen.get(key);
    if (existing) {
      warnings.push(`Etichetă duplicată: "${tag.label}" în ${tag.category}`);
    } else {
      seen.set(key, tag.id);
    }
  }

  return warnings;
}
