import { v4 as uuid } from "uuid";
import type {
  TrackedDeadline,
  DeadlineResolution,
  RegistryEntry,
  DeadlineAuditEntry,
} from "../types";
import { getDeadlineType } from "./deadline-catalog";
import { computeDueDate, addWorkingDays } from "./working-days";

export interface DeadlineDisplayStatus {
  label: string;
  variant: "green" | "yellow" | "red" | "blue" | "gray";
  daysRemaining: number | null;
}

/**
 * Create a new tracked deadline from a type definition + start date.
 */
export function createTrackedDeadline(
  typeId: string,
  startDate: string,
  chainParentId?: string,
): TrackedDeadline | null {
  const def = getDeadlineType(typeId);
  if (!def) return null;

  const start = new Date(startDate);
  start.setHours(0, 0, 0, 0);

  const due = computeDueDate(
    start,
    def.days,
    def.dayType,
    def.isBackwardDeadline,
  );

  return {
    id: uuid(),
    typeId,
    startDate,
    dueDate: formatDate(due),
    resolution: "pending",
    chainParentId,
    auditLog: [
      {
        action: "created",
        timestamp: new Date().toISOString(),
        detail: `Termen creat: ${def.label} (${def.days} ${def.dayType === "working" ? "zile lucrătoare" : "zile calendaristice"})`,
      },
    ],
    createdAt: new Date().toISOString(),
  };
}

/**
 * Resolve a deadline with a given resolution.
 */
export function resolveDeadline(
  deadline: TrackedDeadline,
  resolution: DeadlineResolution,
  note?: string,
): TrackedDeadline {
  const auditEntry: DeadlineAuditEntry = {
    action: "resolved",
    timestamp: new Date().toISOString(),
    detail: `Rezolvat: ${resolution}${note ? ` — ${note}` : ""}`,
  };
  return {
    ...deadline,
    resolution,
    resolvedDate: new Date().toISOString(),
    resolutionNote: note,
    auditLog: [...(deadline.auditLog ?? []), auditEntry],
  };
}

/**
 * Pause a deadline (e.g., when a clarification request is linked).
 * Records the pause date and adds audit entry.
 */
export function pauseDeadline(deadline: TrackedDeadline): TrackedDeadline {
  if (deadline.pausedAt) return deadline; // already paused
  const now = new Date().toISOString();
  return {
    ...deadline,
    pausedAt: now,
    auditLog: [
      ...(deadline.auditLog ?? []),
      {
        action: "paused" as DeadlineAuditEntry["action"],
        timestamp: now,
        detail: "Termen suspendat — solicitare clarificări primită",
      },
    ],
  };
}

/**
 * Resume a paused deadline (e.g., when completions are submitted).
 * Adds the paused days to totalPausedDays and shifts dueDate forward.
 */
export function resumeDeadline(
  deadline: TrackedDeadline,
  resumeDate?: string,
): TrackedDeadline {
  if (!deadline.pausedAt) return deadline; // not paused
  const now = resumeDate ? new Date(resumeDate) : new Date();
  now.setHours(0, 0, 0, 0);
  const pausedStart = new Date(deadline.pausedAt);
  pausedStart.setHours(0, 0, 0, 0);
  const pausedDays = Math.max(
    0,
    Math.round((now.getTime() - pausedStart.getTime()) / (1000 * 60 * 60 * 24)),
  );

  // Shift due date forward by the number of paused days
  const oldDue = new Date(deadline.dueDate);
  oldDue.setHours(0, 0, 0, 0);
  oldDue.setDate(oldDue.getDate() + pausedDays);
  const newDueDate = formatDate(oldDue);

  const totalPaused = (deadline.totalPausedDays ?? 0) + pausedDays;
  const ts = new Date().toISOString();

  return {
    ...deadline,
    pausedAt: undefined,
    totalPausedDays: totalPaused,
    dueDate: newDueDate,
    auditLog: [
      ...(deadline.auditLog ?? []),
      {
        action: "resumed" as DeadlineAuditEntry["action"],
        timestamp: ts,
        detail: `Termen reluat — ${pausedDays} zile suspendate, nou termen: ${newDueDate}`,
      },
    ],
  };
}

/**
 * Get the display status for a tracked deadline — color coding + label.
 */
export function getDeadlineDisplayStatus(
  deadline: TrackedDeadline,
): DeadlineDisplayStatus {
  const def = getDeadlineType(deadline.typeId);

  // Paused — waiting for completions
  if (deadline.pausedAt && deadline.resolution === "pending") {
    return { label: "Suspendat", variant: "blue", daysRemaining: null };
  }

  // Already resolved
  if (deadline.resolution !== "pending") {
    if (deadline.resolution === "aprobat-tacit") {
      return { label: "Aprobat tacit", variant: "blue", daysRemaining: null };
    }
    if (deadline.resolution === "respins") {
      return { label: "Respins", variant: "gray", daysRemaining: null };
    }
    if (deadline.resolution === "anulat") {
      return { label: "Anulat", variant: "gray", daysRemaining: null };
    }
    if (deadline.resolution === "intrerupt") {
      return { label: "Intrerupt", variant: "yellow", daysRemaining: null };
    }
    return { label: "Finalizat", variant: "gray", daysRemaining: null };
  }

  // Pending — compute days remaining
  const now = new Date();
  now.setHours(0, 0, 0, 0);
  const due = new Date(deadline.dueDate);
  due.setHours(0, 0, 0, 0);

  const diff = due.getTime() - now.getTime();
  const daysRemaining = Math.ceil(diff / (1000 * 60 * 60 * 24));

  // Overdue + tacit applicable → tacit approval
  if (daysRemaining < 0 && def?.tacitApprovalApplicable) {
    return { label: "Aprobat tacit", variant: "blue", daysRemaining };
  }

  if (daysRemaining < 0) {
    return { label: "Depășit termen", variant: "red", daysRemaining };
  }
  if (daysRemaining <= 5) {
    return { label: "Urgent", variant: "yellow", daysRemaining };
  }
  return { label: "În termen", variant: "green", daysRemaining };
}

/**
 * Aggregate deadline stats across all entries.
 */
export function aggregateDeadlines(entries: RegistryEntry[]): {
  active: number;
  urgent: number;
  overdue: number;
  tacit: number;
  missingRecipientReg: number;
  expiringSoon: number;
  all: Array<{
    deadline: TrackedDeadline;
    entry: RegistryEntry;
    status: DeadlineDisplayStatus;
  }>;
} {
  let active = 0;
  let urgent = 0;
  let overdue = 0;
  let tacit = 0;
  const all: Array<{
    deadline: TrackedDeadline;
    entry: RegistryEntry;
    status: DeadlineDisplayStatus;
  }> = [];

  // Count entries missing recipient registration (outgoing with deadlines)
  let missingRecipientReg = 0;
  let expiringSoon = 0;
  const now = new Date();
  now.setHours(0, 0, 0, 0);

  for (const entry of entries) {
    // Skip closed entries — their deadlines are no longer actionable
    if (entry.status === "inchis") continue;

    // Check missing recipient registration for outgoing entries
    if (
      entry.direction === "iesit" &&
      entry.status === "deschis" &&
      (entry.trackedDeadlines ?? []).length > 0 &&
      !entry.recipientRegDate
    ) {
      missingRecipientReg++;
    }

    // Check document expiry
    if (entry.expiryDate && entry.status === "deschis") {
      const expiry = new Date(entry.expiryDate);
      expiry.setHours(0, 0, 0, 0);
      const daysLeft = Math.ceil(
        (expiry.getTime() - now.getTime()) / (1000 * 60 * 60 * 24),
      );
      const alertDays = entry.expiryAlertDays ?? 30;
      if (daysLeft <= alertDays) {
        expiringSoon++;
      }
    }

    for (const dl of entry.trackedDeadlines ?? []) {
      // Skip auto-tracked/background sub-deadlines from top-level stats
      const dlDef = getDeadlineType(dl.typeId);
      if (dlDef?.backgroundOnly) continue;

      const status = getDeadlineDisplayStatus(dl);
      all.push({ deadline: dl, entry, status });

      if (dl.resolution === "pending") {
        // Only count user-created (non-auto-track) deadlines in main stats
        if (!dlDef?.autoTrack) {
          active++;
          if (status.variant === "yellow") urgent++;
          if (status.variant === "red") overdue++;
          if (status.variant === "blue") tacit++;
        }
      } else if (dl.resolution === "aprobat-tacit") {
        tacit++;
      }
    }
  }

  // Sort: overdue first, then by due date ascending
  all.sort((a, b) => {
    const aP = a.deadline.resolution === "pending" ? 0 : 1;
    const bP = b.deadline.resolution === "pending" ? 0 : 1;
    if (aP !== bP) return aP - bP;
    return a.deadline.dueDate.localeCompare(b.deadline.dueDate);
  });

  return {
    active,
    urgent,
    overdue,
    tacit,
    missingRecipientReg,
    expiringSoon,
    all,
  };
}

function formatDate(d: Date): string {
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, "0");
  const day = String(d.getDate()).padStart(2, "0");
  return `${y}-${m}-${day}`;
}

// ── Grouped deadlines for simplified dashboard ──

export interface DeadlineEntryGroup {
  entry: RegistryEntry;
  /** Main deadlines (user-created, non-background) */
  mainDeadlines: Array<{
    deadline: TrackedDeadline;
    status: DeadlineDisplayStatus;
    chainChildren: Array<{
      deadline: TrackedDeadline;
      status: DeadlineDisplayStatus;
    }>;
  }>;
}

/**
 * Group deadlines by entry for the simplified dashboard.
 * Only includes entries with pending main deadlines.
 * Auto-tracked / background deadlines are nested under their chain parent.
 */
export function groupDeadlinesByEntry(
  entries: RegistryEntry[],
): DeadlineEntryGroup[] {
  const groups: DeadlineEntryGroup[] = [];

  for (const entry of entries) {
    // Skip closed entries — their deadlines are no longer actionable
    if (entry.status === "inchis") continue;

    const deadlines = entry.trackedDeadlines ?? [];
    if (deadlines.length === 0) continue;

    // Separate: main deadlines vs chain children vs background
    const chainChildMap = new Map<
      string,
      Array<{ deadline: TrackedDeadline; status: DeadlineDisplayStatus }>
    >();
    const mainItems: Array<{
      deadline: TrackedDeadline;
      status: DeadlineDisplayStatus;
      chainChildren: Array<{
        deadline: TrackedDeadline;
        status: DeadlineDisplayStatus;
      }>;
    }> = [];

    // First pass: identify chain children
    for (const dl of deadlines) {
      const def = getDeadlineType(dl.typeId);
      if (def?.backgroundOnly) continue; // skip background-only

      if (dl.chainParentId) {
        const children = chainChildMap.get(dl.chainParentId) ?? [];
        children.push({ deadline: dl, status: getDeadlineDisplayStatus(dl) });
        chainChildMap.set(dl.chainParentId, children);
      }
    }

    // Check if there are any user-selectable (non-autoTrack) deadlines
    const hasUserDeadlines = deadlines.some((dl) => {
      const d = getDeadlineType(dl.typeId);
      return !d?.autoTrack && !d?.backgroundOnly && !dl.chainParentId;
    });

    // Second pass: build main deadline items
    for (const dl of deadlines) {
      const def = getDeadlineType(dl.typeId);
      if (def?.backgroundOnly) continue;
      if (dl.chainParentId) continue; // chain children are nested

      // Auto-tracked: hide when there are user-selectable deadlines (they appear as milestones)
      // Only show auto-tracked as standalone when there are NO user deadlines on the entry
      if (def?.autoTrack) {
        if (hasUserDeadlines) continue;
        if (dl.resolution !== "pending") continue;
      }

      mainItems.push({
        deadline: dl,
        status: getDeadlineDisplayStatus(dl),
        chainChildren: chainChildMap.get(dl.id) ?? [],
      });
    }

    // Only include entries that have pending main deadlines
    const hasPending = mainItems.some((m) => m.deadline.resolution === "pending");
    if (!hasPending) continue;

    groups.push({ entry, mainDeadlines: mainItems });
  }

  // Sort: entries with overdue/urgent deadlines first
  groups.sort((a, b) => {
    const worstA = worstVariant(a.mainDeadlines.map((m) => m.status.variant));
    const worstB = worstVariant(b.mainDeadlines.map((m) => m.status.variant));
    return worstA - worstB;
  });

  return groups;
}

function worstVariant(variants: string[]): number {
  const order: Record<string, number> = {
    red: 0,
    yellow: 1,
    blue: 2,
    green: 3,
    gray: 4,
  };
  let worst = 4;
  for (const v of variants) {
    const o = order[v] ?? 4;
    if (o < worst) worst = o;
  }
  return worst;
}

// ── Transmission status (background check) ──

export type TransmissionStatus = "on-time" | "late" | null;

/**
 * Compute whether a reply entry was transmitted within the legal 1-day window.
 * Returns null if recipient registration date is not available.
 */
export function computeTransmissionStatus(
  parentEntry: RegistryEntry,
  replyEntry: RegistryEntry,
): TransmissionStatus {
  if (!parentEntry.recipientRegDate) return null;

  const regDate = new Date(parentEntry.recipientRegDate);
  regDate.setHours(0, 0, 0, 0);

  // 1 working day after recipient registration date
  const deadline = addWorkingDays(regDate, 1);

  const replyDate = new Date(replyEntry.date);
  replyDate.setHours(0, 0, 0, 0);

  return replyDate.getTime() <= deadline.getTime() ? "on-time" : "late";
}

// ── Auto-resolution of deadlines ──

/**
 * Find deadlines on a parent entry that should be auto-resolved when a reply arrives.
 * Only returns pending deadlines that match the reply context.
 */
export function findAutoResolvableDeadlines(
  parentEntry: RegistryEntry,
  replyEntry: RegistryEntry,
): TrackedDeadline[] {
  const pending = (parentEntry.trackedDeadlines ?? []).filter(
    (dl) => dl.resolution === "pending",
  );
  if (pending.length === 0) return [];

  // Only auto-resolve when reply is incoming (response to our outgoing request)
  if (replyEntry.direction !== "intrat") return [];
  if (parentEntry.direction !== "iesit") return [];

  // Filter: skip background-only and auto-tracked chain children
  return pending.filter((dl) => {
    const def = getDeadlineType(dl.typeId);
    if (!def) return false;
    if (def.backgroundOnly) return false;
    // Include main deadlines (no chain parent) — these are the ones the user cares about
    if (!dl.chainParentId) return true;
    return false;
  });
}
