"use client";

import { useMemo } from "react";
import type { EstimateDemandLineRateMode } from "@capakraken/shared";
import {
  computeEvenSpread,
  rebalanceSpread,
} from "@capakraken/engine";
import {
  getEffectiveDemandLineValues,
} from "~/components/estimates/EstimateWorkspace.calculations.js";
import type { EstimateResourceSnapshotView } from "~/components/estimates/EstimateWorkspace.types.js";
import { InfoTooltip } from "~/components/ui/InfoTooltip.js";
import { formatMoney } from "~/lib/format.js";

const INPUT_CLS =
  "w-full rounded-xl border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 outline-none transition focus:border-brand-500 focus:ring-2 focus:ring-brand-100";
const LABEL_CLS = "mb-1.5 block text-xs font-semibold uppercase tracking-wide text-gray-500";

function toNumber(value: string) {
  const parsed = Number.parseFloat(value);
  return Number.isFinite(parsed) ? parsed : 0;
}

function toCents(value: string) {
  return Math.round(toNumber(value) * 100);
}

export interface EditableDemandLine {
  id?: string;
  scopeItemId?: string;
  roleId?: string;
  resourceId?: string;
  lineType: string;
  name: string;
  chapter: string;
  hours: string;
  currency: string;
  costRate: string;
  billRate: string;
  costRateMode: EstimateDemandLineRateMode;
  billRateMode: EstimateDemandLineRateMode;
  metadata: Record<string, unknown>;
  /** Locked monthly hours overrides, keyed by "YYYY-MM" */
  lockedMonths: Record<string, number>;
  /** Whether the monthly spread section is expanded */
  spreadExpanded: boolean;
}

export function makeDemandLine(): EditableDemandLine {
  return {
    lineType: "LABOR",
    name: "",
    chapter: "",
    hours: "8",
    currency: "EUR",
    costRate: "0",
    billRate: "0",
    costRateMode: "manual",
    billRateMode: "manual",
    metadata: {},
    lockedMonths: {},
    spreadExpanded: false,
  };
}

export interface ResourceOption {
  id: string;
  eid: string;
  displayName: string;
  chapter?: string | null;
  roleId?: string | null;
  lcrCents: number;
  ucrCents: number;
  currency: string;
  dynamicFields?: Record<string, unknown>;
}

export interface DemandLineEditorProps {
  demandLines: EditableDemandLine[];
  onChange: (updater: (current: EditableDemandLine[]) => EditableDemandLine[]) => void;
  resourceOptions: ResourceOption[];
  resourceMap: Map<string, ResourceOption>;
  snapshotByResourceId: Map<string, EstimateResourceSnapshotView>;
  baseCurrency: string;
  projectStartDate: Date | null;
  projectEndDate: Date | null;
  spreadMonths: string[];
  aggregatedSpread: Record<string, number>;
}

type ResourceSnapshotLike = ResourceOption | EstimateResourceSnapshotView;

export function DemandLineEditor({
  demandLines,
  onChange,
  resourceOptions,
  resourceMap,
  snapshotByResourceId,
  baseCurrency,
  projectStartDate,
  projectEndDate,
  spreadMonths,
  aggregatedSpread,
}: DemandLineEditorProps) {
  const hasProjectDates = projectStartDate !== null && projectEndDate !== null;

  function getLineResourceSnapshot(line: EditableDemandLine): ResourceSnapshotLike | null {
    if (!line.resourceId) {
      return null;
    }
    return resourceMap.get(line.resourceId) ?? snapshotByResourceId.get(line.resourceId) ?? null;
  }

  function getLineEffectiveValues(line: EditableDemandLine) {
    return getEffectiveDemandLineValues({
      resourceSnapshot: getLineResourceSnapshot(line),
      hours: toNumber(line.hours),
      currency: line.currency,
      defaultCurrency: baseCurrency,
      costRateCents: toCents(line.costRate),
      billRateCents: toCents(line.billRate),
      costRateMode: line.costRateMode,
      billRateMode: line.billRateMode,
    });
  }

  function updateDemandLine(index: number, updater: (line: EditableDemandLine) => EditableDemandLine) {
    onChange((current) =>
      current.map((entry, entryIndex) => (entryIndex === index ? updater(entry) : entry)),
    );
  }

  function applyResourceSelection(index: number, nextResourceId: string) {
    updateDemandLine(index, (line) => {
      if (!nextResourceId) {
        const { resourceId: _resourceId, ...unlinkedLine } = line;
        return {
          ...unlinkedLine,
          costRateMode: "manual",
          billRateMode: "manual",
        };
      }

      const resource = resourceMap.get(nextResourceId);
      if (!resource) {
        return line;
      }

      return {
        ...line,
        resourceId: resource.id,
        ...(resource.roleId ? { roleId: resource.roleId } : {}),
        chapter: resource.chapter ?? line.chapter,
        currency: resource.currency,
        costRate: (resource.lcrCents / 100).toFixed(2),
        billRate: (resource.ucrCents / 100).toFixed(2),
        costRateMode: "resource",
        billRateMode: "resource",
        name: line.name.trim() ? line.name : resource.displayName,
      };
    });
  }

  function syncDemandLineRates(index: number) {
    updateDemandLine(index, (line) => {
      if (!line.resourceId) {
        return line;
      }

      const resource = resourceMap.get(line.resourceId);
      if (!resource) {
        return line;
      }

      return {
        ...line,
        chapter: resource.chapter ?? line.chapter,
        currency: resource.currency,
        costRate: (resource.lcrCents / 100).toFixed(2),
        billRate: (resource.ucrCents / 100).toFixed(2),
        costRateMode: "resource",
        billRateMode: "resource",
      };
    });
  }

  function setDemandLineRateMode(
    index: number,
    rateField: "costRateMode" | "billRateMode",
    nextMode: EstimateDemandLineRateMode,
  ) {
    updateDemandLine(index, (line) => {
      const resourceSnapshot = getLineResourceSnapshot(line);

      if (!resourceSnapshot || nextMode === "manual") {
        return {
          ...line,
          [rateField]: "manual",
        };
      }

      return {
        ...line,
        [rateField]: "resource",
        ...(rateField === "costRateMode"
          ? { costRate: (resourceSnapshot.lcrCents / 100).toFixed(2) }
          : { billRate: (resourceSnapshot.ucrCents / 100).toFixed(2) }),
        ...(resourceSnapshot.currency ? { currency: resourceSnapshot.currency } : {}),
      };
    });
  }

  function syncAllLiveLinkedLines() {
    onChange((current) =>
      current.map((line) => {
        const resourceSnapshot = getLineResourceSnapshot(line);

        if (!resourceSnapshot) {
          return line;
        }

        return {
          ...line,
          currency: resourceSnapshot.currency,
          costRate:
            line.costRateMode === "resource"
              ? (resourceSnapshot.lcrCents / 100).toFixed(2)
              : line.costRate,
          billRate:
            line.billRateMode === "resource"
              ? (resourceSnapshot.ucrCents / 100).toFixed(2)
              : line.billRate,
        };
      }),
    );
  }

  function computeLineSpread(line: EditableDemandLine): Record<string, number> {
    if (!hasProjectDates) return {};
    const hours = toNumber(line.hours);
    if (hours <= 0) return {};

    const lockedKeys = Object.keys(line.lockedMonths);
    if (lockedKeys.length > 0) {
      return rebalanceSpread({
        totalHours: hours,
        startDate: projectStartDate,
        endDate: projectEndDate,
        lockedMonths: line.lockedMonths,
      }).spread;
    }

    return computeEvenSpread({
      totalHours: hours,
      startDate: projectStartDate,
      endDate: projectEndDate,
    }).spread;
  }

  function toggleMonthLock(lineIndex: number, monthKey: string, currentValue: number) {
    updateDemandLine(lineIndex, (line) => {
      const isLocked = monthKey in line.lockedMonths;
      if (isLocked) {
        const { [monthKey]: _removed, ...rest } = line.lockedMonths;
        return { ...line, lockedMonths: rest };
      }
      return {
        ...line,
        lockedMonths: { ...line.lockedMonths, [monthKey]: currentValue },
      };
    });
  }

  function setLockedMonthValue(lineIndex: number, monthKey: string, value: string) {
    const numValue = Math.max(0, toNumber(value));
    updateDemandLine(lineIndex, (line) => ({
      ...line,
      lockedMonths: { ...line.lockedMonths, [monthKey]: Math.round(numValue * 10) / 10 },
    }));
  }

  return (
    <div className="space-y-4">
      <div className="flex justify-end">
        <button
          type="button"
          className="rounded-2xl border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700"
          onClick={syncAllLiveLinkedLines}
        >
          Recalculate live-linked lines
        </button>
      </div>
      {demandLines.map((line, index) => {
        const linkedResource = line.resourceId ? getLineResourceSnapshot(line) : null;
        const effectiveValues = getLineEffectiveValues(line);
        const costDeltaCents =
          linkedResource != null ? toCents(line.costRate) - linkedResource.lcrCents : 0;
        const billDeltaCents =
          linkedResource != null ? toCents(line.billRate) - linkedResource.ucrCents : 0;

        return (
          <div key={line.id ?? `line-${index}`} className="rounded-3xl border border-gray-200 bg-white p-5 shadow-sm">
            <div className="mb-4 flex flex-wrap items-center justify-between gap-3 rounded-2xl bg-gray-50 px-4 py-3">
              <div>
                <p className="text-xs font-semibold uppercase tracking-wide text-gray-500">Resource link</p>
                <p className="mt-1 text-sm text-gray-700">
                  {linkedResource
                    ? `${linkedResource.displayName} (${("eid" in linkedResource ? linkedResource.eid : (linkedResource as EstimateResourceSnapshotView).sourceEid) ?? "snapshot"})`
                    : "This demand line is currently unlinked."}
                </p>
              </div>
              {linkedResource && (
                <div className="flex items-center gap-2">
                  <span
                    className={
                      line.costRateMode === "resource" && line.billRateMode === "resource"
                        ? "rounded-full bg-emerald-100 px-3 py-1 text-xs font-semibold text-emerald-700"
                        : "rounded-full bg-amber-100 px-3 py-1 text-xs font-semibold text-amber-700"
                    }
                  >
                    {line.costRateMode === "resource" && line.billRateMode === "resource"
                      ? "Live rates synced"
                      : "Manual override active"}
                  </span>
                  <button
                    type="button"
                    className="rounded-xl border border-gray-300 bg-white px-3 py-1.5 text-xs font-medium text-gray-700"
                    onClick={() => syncDemandLineRates(index)}
                  >
                    Apply current resource rates
                  </button>
                </div>
              )}
            </div>

            <div className="mb-4 grid gap-4 md:grid-cols-2">
              <label>
                <span className={LABEL_CLS}>Linked resource <InfoTooltip content="Link to a CapaKraken resource. Live-linked rates refresh automatically; manual overrides are persisted." /></span>
                <select
                  className={INPUT_CLS}
                  value={line.resourceId ?? ""}
                  onChange={(event) => applyResourceSelection(index, event.target.value)}
                >
                  <option value="">Unlinked</option>
                  {resourceOptions.map((resource) => (
                    <option key={resource.id} value={resource.id}>
                      {resource.displayName} ({resource.eid})
                    </option>
                  ))}
                </select>
              </label>
              <div className="rounded-2xl bg-gray-50 px-4 py-3">
                <p className="text-xs uppercase tracking-wide text-gray-400">Snapshot behavior</p>
                <p className="mt-1 text-sm text-gray-700">
                  Linked resources refresh from live CapaKraken rates when a rate is set to live mode. Manual overrides are persisted on the demand line.
                </p>
              </div>
            </div>

            <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
              <label>
                <span className={LABEL_CLS}>Name <InfoTooltip content="Descriptive label for this demand line, e.g. role name or resource name." /></span>
                <input className={INPUT_CLS} value={line.name} onChange={(event) => updateDemandLine(index, (entry) => ({ ...entry, name: event.target.value }))} />
              </label>
              <label>
                <span className={LABEL_CLS}>Line type <InfoTooltip content="Classification of the demand, typically LABOR. Can also be EXPENSE or SUBCONTRACTOR." /></span>
                <input className={INPUT_CLS} value={line.lineType} onChange={(event) => updateDemandLine(index, (entry) => ({ ...entry, lineType: event.target.value }))} />
              </label>
              <label>
                <span className={LABEL_CLS}>Chapter <InfoTooltip content="Department or cost center. Auto-filled when a resource is linked." /></span>
                <input className={INPUT_CLS} value={line.chapter} onChange={(event) => updateDemandLine(index, (entry) => ({ ...entry, chapter: event.target.value }))} />
              </label>
              <label>
                <span className={LABEL_CLS}>Hours <InfoTooltip content="Estimated effort in hours. Cost total = hours x cost rate. Price total = hours x sell rate." /></span>
                <input className={INPUT_CLS} value={line.hours} onChange={(event) => updateDemandLine(index, (entry) => ({ ...entry, hours: event.target.value }))} />
              </label>
              <label>
                <span className={LABEL_CLS}>Currency <InfoTooltip content="ISO 4217 currency code for this line's rates (e.g. EUR, USD)." /></span>
                <input className={INPUT_CLS} maxLength={3} value={line.currency} onChange={(event) => updateDemandLine(index, (entry) => ({ ...entry, currency: event.target.value.toUpperCase() }))} />
              </label>
              <label>
                <span className={LABEL_CLS}>Cost rate <InfoTooltip content="Internal hourly cost rate. 'Live' syncs from the resource; 'Manual' allows a custom override. Line cost = hours x cost rate." /></span>
                <div className="space-y-2">
                  <select
                    className={INPUT_CLS}
                    value={line.costRateMode}
                    onChange={(event) =>
                      setDemandLineRateMode(index, "costRateMode", event.target.value as EstimateDemandLineRateMode)
                    }
                  >
                    {getLineResourceSnapshot(line) && <option value="resource">Use live resource rate</option>}
                    <option value="manual">Manual override</option>
                  </select>
                  <input
                    className={INPUT_CLS}
                    value={line.costRate}
                    onChange={(event) =>
                      updateDemandLine(index, (entry) => ({
                        ...entry,
                        costRateMode: "manual",
                        costRate: event.target.value,
                      }))
                    }
                  />
                  {linkedResource && (
                    <p className="text-xs text-gray-500">
                      Live snapshot {formatMoney(linkedResource.lcrCents, linkedResource.currency)}
                      {line.costRateMode === "manual" && costDeltaCents !== 0
                        ? ` (${costDeltaCents > 0 ? "+" : ""}${formatMoney(costDeltaCents, linkedResource.currency)} delta)`
                        : ""}
                    </p>
                  )}
                </div>
              </label>
              <label>
                <span className={LABEL_CLS}>Bill rate <InfoTooltip content="Client-facing hourly sell rate. 'Live' syncs from the resource; 'Manual' allows a custom override. Line price = hours x bill rate." /></span>
                <div className="space-y-2">
                  <select
                    className={INPUT_CLS}
                    value={line.billRateMode}
                    onChange={(event) =>
                      setDemandLineRateMode(index, "billRateMode", event.target.value as EstimateDemandLineRateMode)
                    }
                  >
                    {getLineResourceSnapshot(line) && <option value="resource">Use live resource rate</option>}
                    <option value="manual">Manual override</option>
                  </select>
                  <input
                    className={INPUT_CLS}
                    value={line.billRate}
                    onChange={(event) =>
                      updateDemandLine(index, (entry) => ({
                        ...entry,
                        billRateMode: "manual",
                        billRate: event.target.value,
                      }))
                    }
                  />
                  {linkedResource && (
                    <p className="text-xs text-gray-500">
                      Live snapshot {formatMoney(linkedResource.ucrCents, linkedResource.currency)}
                      {line.billRateMode === "manual" && billDeltaCents !== 0
                        ? ` (${billDeltaCents > 0 ? "+" : ""}${formatMoney(billDeltaCents, linkedResource.currency)} delta)`
                        : ""}
                    </p>
                  )}
                </div>
              </label>
              <div className="rounded-2xl bg-gray-50 px-4 py-3">
                <p className="text-xs uppercase tracking-wide text-gray-400">Cost total <InfoTooltip content="hours x cost rate, stored in cents." /></p>
                <p className="mt-1 text-sm font-semibold text-gray-900">
                  {formatMoney(effectiveValues.costTotalCents, effectiveValues.currency)}
                </p>
                <p className="mt-3 text-xs uppercase tracking-wide text-gray-400">Price total <InfoTooltip content="hours x sell rate, stored in cents." /></p>
                <p className="mt-1 text-sm font-semibold text-gray-900">
                  {formatMoney(effectiveValues.priceTotalCents, effectiveValues.currency)}
                </p>
              </div>
            </div>
            {hasProjectDates && spreadMonths.length > 0 && (
              <div className="mt-4">
                <button
                  type="button"
                  className="flex items-center gap-1.5 text-xs font-medium text-gray-600"
                  onClick={() => updateDemandLine(index, (entry) => ({ ...entry, spreadExpanded: !entry.spreadExpanded }))}
                >
                  <span className={`inline-block transition-transform ${line.spreadExpanded ? "rotate-90" : ""}`}>&#9654;</span>
                  Monthly phasing ({spreadMonths.length} months)
                </button>
                {line.spreadExpanded && (() => {
                  const lineSpread = computeLineSpread(line);
                  return (
                    <div className="mt-3 overflow-x-auto">
                      <table className="w-full text-sm">
                        <thead>
                          <tr className="border-b border-gray-200">
                            <th className="px-2 py-1.5 text-left text-xs font-semibold uppercase tracking-wide text-gray-400">Month</th>
                            <th className="px-2 py-1.5 text-right text-xs font-semibold uppercase tracking-wide text-gray-400">Hours</th>
                            <th className="px-2 py-1.5 text-center text-xs font-semibold uppercase tracking-wide text-gray-400">Lock</th>
                          </tr>
                        </thead>
                        <tbody>
                          {spreadMonths.map((monthKey) => {
                            const isLocked = monthKey in line.lockedMonths;
                            const value = lineSpread[monthKey] ?? 0;
                            return (
                              <tr key={monthKey} className="border-b border-gray-100">
                                <td className="px-2 py-1.5 text-gray-700">{monthKey}</td>
                                <td className="px-2 py-1.5 text-right">
                                  {isLocked ? (
                                    <input
                                      className="w-20 rounded border border-amber-300 bg-amber-50 px-2 py-1 text-right text-sm text-gray-900"
                                      value={line.lockedMonths[monthKey]}
                                      onChange={(event) => setLockedMonthValue(index, monthKey, event.target.value)}
                                    />
                                  ) : (
                                    <span className="text-gray-700">{value.toFixed(1)}</span>
                                  )}
                                </td>
                                <td className="px-2 py-1.5 text-center">
                                  <button
                                    type="button"
                                    className={`rounded px-2 py-0.5 text-xs font-medium ${isLocked ? "bg-amber-100 text-amber-700" : "bg-gray-100 text-gray-500"}`}
                                    onClick={() => toggleMonthLock(index, monthKey, value)}
                                  >
                                    {isLocked ? "Locked" : "Auto"}
                                  </button>
                                </td>
                              </tr>
                            );
                          })}
                        </tbody>
                        <tfoot>
                          <tr className="border-t border-gray-300">
                            <td className="px-2 py-1.5 text-xs font-semibold uppercase text-gray-500">Total</td>
                            <td className="px-2 py-1.5 text-right text-sm font-semibold text-gray-900">
                              {Object.values(lineSpread).reduce((a, b) => a + b, 0).toFixed(1)}
                            </td>
                            <td />
                          </tr>
                        </tfoot>
                      </table>
                    </div>
                  );
                })()}
              </div>
            )}
            <div className="mt-4 flex justify-end">
              <button type="button" className="text-sm font-medium text-rose-600" onClick={() => onChange((current) => current.filter((_, entryIndex) => entryIndex !== index))}>
                Remove demand line
              </button>
            </div>
          </div>
        );
      })}

      <button type="button" className="rounded-2xl border border-dashed border-gray-300 px-4 py-3 text-sm font-medium text-gray-600" onClick={() => onChange((current) => [...current, makeDemandLine()])}>
        Add demand line
      </button>

      {hasProjectDates && spreadMonths.length > 0 && demandLines.length > 0 && (
        <div className="rounded-3xl border border-gray-200 bg-white p-5 shadow-sm">
          <p className="mb-3 text-sm font-semibold text-gray-900">Aggregated monthly phasing</p>
          <div className="overflow-x-auto">
            <table className="w-full text-sm">
              <thead>
                <tr className="border-b border-gray-200">
                  <th className="px-2 py-1.5 text-left text-xs font-semibold uppercase tracking-wide text-gray-400">Month</th>
                  <th className="px-2 py-1.5 text-right text-xs font-semibold uppercase tracking-wide text-gray-400">Total hours</th>
                </tr>
              </thead>
              <tbody>
                {spreadMonths.map((monthKey) => (
                  <tr key={monthKey} className="border-b border-gray-100">
                    <td className="px-2 py-1.5 text-gray-700">{monthKey}</td>
                    <td className="px-2 py-1.5 text-right text-gray-900">{(aggregatedSpread[monthKey] ?? 0).toFixed(1)}</td>
                  </tr>
                ))}
              </tbody>
              <tfoot>
                <tr className="border-t border-gray-300">
                  <td className="px-2 py-1.5 text-xs font-semibold uppercase text-gray-500">Grand total</td>
                  <td className="px-2 py-1.5 text-right text-sm font-semibold text-gray-900">
                    {Object.values(aggregatedSpread).reduce((a, b) => a + b, 0).toFixed(1)}
                  </td>
                </tr>
              </tfoot>
            </table>
          </div>
        </div>
      )}
    </div>
  );
}
