import React from "react";
import {
  EarningsPanelData,
  EarningsPeriodOption,
  Frequency,
} from "../../types/financial";
import {
  StatementTableMinimal,
  formatNumber,
  formatPercent,
} from "../ui/StatementTableMinimal";
import {
  type UnitOption,
  type UnitScale,
  UNIT_OPTIONS,
  computeAutoUnit,
  formatScaled,
  scaleWarningNumbers,
  unitLabel,
} from "../ui/UnitScale";
import { InlineMappingEditor } from "../Mappings/InlineMappingEditor";
import type { MappingCellInfo } from "../../hooks/useEarningsMappings";

interface EarningsPanelProps {
  data: EarningsPanelData;
  onRunCommand?: (command: string) => void;
}

interface MappingEditorPosition {
  cell: MappingCellInfo;
  top: number;
  left: number;
}

export const buildEarningsRangeCommand = (
  symbol: string,
  frequency: Frequency,
  start: string,
  end: string,
  currency?: string,
) =>
  buildEarningsCommand(symbol, frequency, {
    start,
    end,
    currency,
  });

export const buildEarningsCommand = (
  symbol: string,
  frequency: Frequency,
  options: {
    start?: string;
    end?: string;
    currency?: string;
  } = {},
) => {
  const parts = ["/em", symbol, frequency];

  if (options.start && options.end) {
    parts.push(options.start, options.end);
  }
  if (options.currency) {
    parts.push("--currency", options.currency.toUpperCase());
  }

  return parts.join(" ");
};

export const reconcileEarningsRange = (
  start: string,
  end: string,
  periods: EarningsPeriodOption[],
) => {
  const periodIndexes = new Map(
    periods.map((period, index) => [period.id, index]),
  );
  const startIndex = periodIndexes.get(start);
  const endIndex = periodIndexes.get(end);

  if (startIndex === undefined || endIndex === undefined) {
    return { start, end };
  }

  if (startIndex <= endIndex) {
    return { start, end };
  }

  return { start: end, end };
};

const SourceAttribution: React.FC<{
  status: EarningsPanelData["sourceStatus"];
}> = ({ status }) => {
  return (
    <div className="text-[11px] font-mono text-term-text-muted">
      SEC EDGAR • filing XBRL
      {status.degradedReason && (
        <div className="mt-1 text-warning">{status.degradedReason}</div>
      )}
    </div>
  );
};

export const EarningsPanel: React.FC<EarningsPanelProps> = ({
  data,
  onRunCommand,
}) => {
  const [selectedStart, setSelectedStart] = React.useState(
    data.selectedPeriodStart ?? "",
  );
  const [selectedEnd, setSelectedEnd] = React.useState(
    data.selectedPeriodEnd ?? "",
  );
  const [selectedCurrency, setSelectedCurrency] = React.useState(
    data.selectedCurrency ?? data.availableCurrencies[0] ?? "",
  );
  const [selectedUnit, setSelectedUnit] = React.useState<UnitOption>("auto");
  const [mappingEditor, setMappingEditor] =
    React.useState<MappingEditorPosition | null>(null);
  const [showSuccessToast, setShowSuccessToast] = React.useState(false);
  const toastTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(
    null,
  );

  const hasRangeControls =
    data.availablePeriods.length > 0 &&
    Boolean(data.selectedPeriodStart) &&
    Boolean(data.selectedPeriodEnd);
  const resolvedCurrency =
    selectedCurrency || data.selectedCurrency || data.availableCurrencies[0];
  const headerCurrency =
    data.selectedCurrency ?? data.availableCurrencies[0] ?? undefined;
  const effectiveUnit: UnitScale =
    selectedUnit === "auto"
      ? computeAutoUnit(data.periods.map((p) => p.totalRevenues))
      : selectedUnit;

  const displayPeriods = React.useMemo(
    () =>
      data.periods.map((period) => ({
        ...period,
        label: period.displayLabel || period.label,
      })),
    [data.periods],
  );

  React.useEffect(() => {
    setSelectedStart((current) =>
      current === (data.selectedPeriodStart ?? "")
        ? current
        : (data.selectedPeriodStart ?? ""),
    );
    setSelectedEnd((current) =>
      current === (data.selectedPeriodEnd ?? "")
        ? current
        : (data.selectedPeriodEnd ?? ""),
    );
  }, [data.selectedPeriodEnd, data.selectedPeriodStart]);

  React.useEffect(() => {
    setSelectedCurrency((current) =>
      current === (data.selectedCurrency ?? data.availableCurrencies[0] ?? "")
        ? current
        : (data.selectedCurrency ?? data.availableCurrencies[0] ?? ""),
    );
  }, [data.availableCurrencies, data.selectedCurrency]);

  React.useEffect(() => {
    return () => {
      if (toastTimeoutRef.current) {
        clearTimeout(toastTimeoutRef.current);
      }
    };
  }, []);

  React.useEffect(() => {
    setMappingEditor(null);
  }, [data.symbol, data.frequency]);

  const handleMappingSuccess = React.useCallback(() => {
    if (toastTimeoutRef.current) {
      clearTimeout(toastTimeoutRef.current);
    }

    setShowSuccessToast(true);
    toastTimeoutRef.current = setTimeout(() => {
      setShowSuccessToast(false);
      toastTimeoutRef.current = null;
    }, 3000);
  }, []);

  const commitRange = React.useCallback(
    (start: string, end: string, currency = resolvedCurrency) => {
      if (!onRunCommand || !start || !end) {
        return;
      }

      const normalizedRange = reconcileEarningsRange(
        start,
        end,
        data.availablePeriods,
      );

      if (
        normalizedRange.start === (data.selectedPeriodStart ?? "") &&
        normalizedRange.end === (data.selectedPeriodEnd ?? "")
      ) {
        return;
      }

      const nextCommand = buildEarningsRangeCommand(
        data.symbol,
        data.frequency,
        normalizedRange.start,
        normalizedRange.end,
        currency,
      );
      onRunCommand(nextCommand);
    },
    [
      data.availablePeriods,
      data.frequency,
      data.selectedPeriodEnd,
      data.selectedPeriodStart,
      data.symbol,
      onRunCommand,
      resolvedCurrency,
    ],
  );

  const handleStartChange = React.useCallback(
    (nextStart: string) => {
      const nextRange = reconcileEarningsRange(
        nextStart,
        selectedEnd || nextStart,
        data.availablePeriods,
      );

      setSelectedStart(nextRange.start);
      setSelectedEnd(nextRange.end);
      commitRange(nextRange.start, nextRange.end);
    },
    [commitRange, data.availablePeriods, selectedEnd],
  );

  const handleEndChange = React.useCallback(
    (nextEnd: string) => {
      const nextRange = reconcileEarningsRange(
        selectedStart || nextEnd,
        nextEnd,
        data.availablePeriods,
      );

      setSelectedStart(nextRange.start);
      setSelectedEnd(nextRange.end);
      commitRange(nextRange.start, nextRange.end);
    },
    [commitRange, data.availablePeriods, selectedStart],
  );

  const handleOpenMappingEditor = React.useCallback(
    (cellInfo: MappingCellInfo) => {
      setMappingEditor({
        cell: cellInfo,
        top: 200,
        left: 400,
      });
    },
    [],
  );

  const handleCurrencyChange = React.useCallback(
    (nextCurrency: string) => {
      const normalizedCurrency = nextCurrency.toUpperCase();
      setSelectedCurrency(normalizedCurrency);

      if (!onRunCommand) {
        return;
      }

      onRunCommand(
        buildEarningsCommand(data.symbol, data.frequency, {
          start: selectedStart || data.selectedPeriodStart,
          end: selectedEnd || data.selectedPeriodEnd,
          currency: normalizedCurrency,
        }),
      );
    },
    [
      data.frequency,
      data.selectedPeriodEnd,
      data.selectedPeriodStart,
      data.symbol,
      onRunCommand,
      selectedEnd,
      selectedStart,
    ],
  );

  const metrics = React.useMemo(
    () => [
      {
        key: "totalRevenues",
        label: "Total Revenues",
        mappable: true,
        render: (period: EarningsPanelData["periods"][number]) =>
          formatScaled(period.totalRevenues, effectiveUnit),
      },
      {
        key: "totalRevenuesYoyChangePercent",
        label: "Total Revenues %Chg",
        render: (period: EarningsPanelData["periods"][number]) =>
          formatPercent(period.totalRevenuesYoyChangePercent),
      },
      {
        key: "costOfSales",
        label: "Cost of Sales",
        mappable: true,
        render: (period: EarningsPanelData["periods"][number]) =>
          formatScaled(period.costOfSales, effectiveUnit),
      },
      {
        key: "grossProfit",
        label: "Gross Profit",
        mappable: true,
        render: (period: EarningsPanelData["periods"][number]) =>
          formatScaled(period.grossProfit, effectiveUnit),
      },
      {
        key: "grossProfitMargin",
        label: "Gross Profit Margin",
        render: (period: EarningsPanelData["periods"][number]) =>
          formatPercent(period.grossProfitMargin),
      },
      {
        key: "sellingGeneralAndAdministrativeExpenses",
        label: "Selling, General & Administrative Expenses",
        mappable: true,
        render: (period: EarningsPanelData["periods"][number]) =>
          formatScaled(
            period.sellingGeneralAndAdministrativeExpenses,
            effectiveUnit,
          ),
      },
      {
        key: "researchAndDevelopmentExpenses",
        label: "Research & Development Expenses",
        mappable: true,
        render: (period: EarningsPanelData["periods"][number]) =>
          formatScaled(
            period.researchAndDevelopmentExpenses,
            effectiveUnit,
          ),
      },
      {
        key: "otherOperatingExpenses",
        label: "Other Operating Expenses",
        mappable: true,
        render: (period: EarningsPanelData["periods"][number]) =>
          formatScaled(period.otherOperatingExpenses, effectiveUnit),
      },
      {
        key: "operatingProfit",
        label: "Operating Profit",
        mappable: true,
        render: (period: EarningsPanelData["periods"][number]) =>
          formatScaled(period.operatingProfit, effectiveUnit),
      },
      {
        key: "operatingMargin",
        label: "Operating Margin",
        render: (period: EarningsPanelData["periods"][number]) =>
          formatPercent(period.operatingMargin),
      },
      {
        key: "nonOperatingIncome",
        label: "Non-Operating Income",
        mappable: true,
        render: (period: EarningsPanelData["periods"][number]) =>
          formatScaled(period.nonOperatingIncome, effectiveUnit),
      },
      {
        key: "incomeBeforeProvisionForIncomeTaxes",
        label: "Income Before Provision for Income Taxes",
        mappable: true,
        render: (period: EarningsPanelData["periods"][number]) =>
          formatScaled(
            period.incomeBeforeProvisionForIncomeTaxes,
            effectiveUnit,
          ),
      },
      {
        key: "provisionForIncomeTaxes",
        label: "Provision for Income Taxes",
        mappable: true,
        render: (period: EarningsPanelData["periods"][number]) =>
          formatScaled(period.provisionForIncomeTaxes, effectiveUnit),
      },
      {
        key: "consolidatedNetIncome",
        label: "Consolidated Net Income",
        mappable: true,
        render: (period: EarningsPanelData["periods"][number]) =>
          formatScaled(period.consolidatedNetIncome, effectiveUnit),
      },
      {
        key: "netIncomeAttributableToCommonShareholders",
        label: "Net Income Attributable to Common Shareholders",
        mappable: true,
        render: (period: EarningsPanelData["periods"][number]) =>
          formatScaled(
            period.netIncomeAttributableToCommonShareholders,
            effectiveUnit,
          ),
      },
      {
        key: "basicEps",
        label: "Basic EPS",
        mappable: true,
        render: (period: EarningsPanelData["periods"][number]) =>
          formatNumber(period.basicEps),
      },
      {
        key: "dilutedEps",
        label: "Diluted EPS",
        mappable: true,
        render: (period: EarningsPanelData["periods"][number]) =>
          formatNumber(period.dilutedEps),
      },
      {
        key: "basicWeightedAverageSharesOutstanding",
        label: "Basic Weighted Average Shares Outstanding",
        mappable: true,
        render: (period: EarningsPanelData["periods"][number]) =>
          formatNumber(period.basicWeightedAverageSharesOutstanding),
      },
      {
        key: "totalSharesOutstanding",
        label: "Total Shares Outstanding",
        mappable: true,
        render: (period: EarningsPanelData["periods"][number]) =>
          formatNumber(period.totalSharesOutstanding),
      },
      {
        key: "dilutedWeightedAverageSharesOutstanding",
        label: "Diluted Weighted Average Shares Outstanding",
        mappable: true,
        render: (period: EarningsPanelData["periods"][number]) =>
          formatNumber(period.dilutedWeightedAverageSharesOutstanding),
      },
      {
        key: "ebitda",
        label: "EBITDA",
        mappable: true,
        render: (period: EarningsPanelData["periods"][number]) =>
          formatScaled(period.ebitda, effectiveUnit),
      },
      {
        key: "effectiveTaxRate",
        label: "Effective Tax Rate",
        mappable: true,
        render: (period: EarningsPanelData["periods"][number]) =>
          formatPercent(period.effectiveTaxRate),
      },
    ],
    [effectiveUnit],
  );

  return (
    <div className="earnings-panel overflow-auto py-4">
      <header className="mb-4">
        <div className="flex items-start justify-between">
          <div>
            <h2 className="text-heading-lg text-term-text">
              {data.symbol} Earnings
            </h2>
            <p className="mt-1 text-body-xs text-term-text-muted">
              {data.companyName} • {data.frequency}
            </p>
            {headerCurrency && (
              <div className="mt-3 flex items-center gap-2 text-body-xs text-term-text-muted">
                <span className="font-mono uppercase tracking-[0.18em] text-[10px]">
                  Currency
                </span>
                {data.availableCurrencies.length > 1 && onRunCommand ? (
                  <select
                    aria-label="Earnings currency"
                    className="rounded-md border border-term-border-subtle bg-term-bg px-2 py-1 text-term-text"
                    onChange={(event) =>
                      handleCurrencyChange(event.currentTarget.value)
                    }
                    value={selectedCurrency}
                  >
                    {data.availableCurrencies.map((currency) => (
                      <option key={currency} value={currency}>
                        {currency}
                      </option>
                    ))}
                  </select>
                ) : (
                  <span className="rounded border border-term-border-subtle bg-term-bg-secondary/40 px-2 py-1 font-mono text-term-text">
                    {headerCurrency}
                  </span>
                )}
              </div>
            )}
            <div className="mt-1 flex items-center gap-2 text-body-xs text-term-text-muted">
              <span className="font-mono uppercase tracking-[0.18em] text-[10px]">
                Units
              </span>
              <select
                aria-label="Earnings units"
                className="rounded-md border border-term-border-subtle bg-term-bg px-2 py-1 text-term-text"
                onChange={(event) =>
                  setSelectedUnit(event.currentTarget.value as UnitOption)
                }
                value={selectedUnit}
              >
                {UNIT_OPTIONS.map((opt) => (
                  <option key={opt.value} value={opt.value}>
                    {opt.label}
                  </option>
                ))}
              </select>
            </div>
          </div>
          <div className="text-right text-body-xs">
            <div className="text-term-text-muted">CIK {data.cik}</div>
            {data.latestFiling && (
              <div className="mt-1 text-term-text-tertiary">
                {data.latestFiling.form} filed {data.latestFiling.filingDate}
              </div>
            )}
          </div>
        </div>
      </header>

      <section>
        {hasRangeControls && (
          <div className="mb-4 rounded-lg border border-term-border-subtle bg-term-bg-secondary/40 px-3 py-3">
            <div className="mb-3 text-[10px] font-mono uppercase tracking-[0.18em] text-term-text-muted">
              Reported Range
            </div>
            <div className="grid gap-3 md:grid-cols-2">
              <label className="block text-body-xs text-term-text-muted">
                <span className="mb-1 block font-mono uppercase tracking-[0.18em] text-[10px]">
                  Start Period
                </span>
                <select
                  aria-label="Start earnings period"
                  className="w-full rounded-md border border-term-border-subtle bg-term-bg px-3 py-2 text-term-text"
                  onChange={(event) =>
                    handleStartChange(event.currentTarget.value)
                  }
                  value={selectedStart}
                >
                  {data.availablePeriods.map((period) => (
                    <option key={period.id} value={period.id}>
                      {period.label}
                    </option>
                  ))}
                </select>
              </label>
              <label className="block text-body-xs text-term-text-muted">
                <span className="mb-1 block font-mono uppercase tracking-[0.18em] text-[10px]">
                  End Period
                </span>
                <select
                  aria-label="End earnings period"
                  className="w-full rounded-md border border-term-border-subtle bg-term-bg px-3 py-2 text-term-text"
                  onChange={(event) =>
                    handleEndChange(event.currentTarget.value)
                  }
                  value={selectedEnd}
                >
                  {data.availablePeriods.map((period) => (
                    <option key={period.id} value={period.id}>
                      {period.label}
                    </option>
                  ))}
                </select>
              </label>
            </div>
          </div>
        )}
        <div className="mb-2 text-[10px] font-mono uppercase tracking-[0.18em] text-term-text-muted">
          Values in {unitLabel(effectiveUnit)}{headerCurrency ? ` \u00B7 ${headerCurrency}` : ''}
        </div>
        <StatementTableMinimal
          periods={displayPeriods}
          enableMappingCustomizer={true}
          onMapField={handleOpenMappingEditor}
          metrics={metrics}
        />
      </section>

      <footer className="mt-4 border-t border-term-border-subtle pt-4">
        <SourceAttribution status={data.sourceStatus} />
        {data.mappingWarnings && data.mappingWarnings.length > 0 && (
          <div className="mt-2 rounded border border-warning/30 bg-warning/5 px-3 py-2">
            <div className="text-[11px] font-mono font-semibold text-warning mb-1">
              Mapping &amp; Calculation Warnings
            </div>
            <ul className="text-[11px] font-mono text-term-text-muted list-disc list-inside">
              {data.mappingWarnings.map((warning, index) => (
                <li key={index}>{scaleWarningNumbers(warning, effectiveUnit)}</li>
              ))}
            </ul>
          </div>
        )}
      </footer>

      {/* Success Toast */}
      {showSuccessToast && (
        <div
          className="fixed bottom-4 right-4 z-50 rounded-md bg-success-bg border border-success-border px-4 py-3 shadow-lg"
          aria-live="polite"
          role="status"
        >
          <div className="flex items-center gap-3">
            <div className="text-success-text">
              <svg
                className="h-5 w-5"
                fill="none"
                stroke="currentColor"
                viewBox="0 0 24 24"
              >
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  strokeWidth={2}
                  d="M5 13l4 4L19 7"
                />
              </svg>
            </div>
            <div>
              <p className="text-sm font-medium text-success-text">
                Mapping saved successfully
              </p>
              <p className="mt-1 text-xs text-success-text-muted">
                Earnings data is being refreshed...
              </p>
            </div>
          </div>
        </div>
      )}

      {/* Mapping Editor Overlay */}
      {mappingEditor && (
        <>
          <div
            className="fixed inset-0 z-40 bg-black/20"
            onClick={() => setMappingEditor(null)}
            onKeyDown={(event) => {
              if (
                event.key === "Escape" ||
                event.key === "Enter" ||
                event.key === " "
              ) {
                setMappingEditor(null);
              }
            }}
            role="button"
            tabIndex={0}
            aria-label="Close mapping editor"
            style={{ pointerEvents: "auto" }}
          />
          <InlineMappingEditor
            key={`${mappingEditor.cell.metricKey}-${mappingEditor.cell.metricLabel}`}
            cellInfo={mappingEditor.cell}
            context={{
              symbol: data.symbol,
              frequency: data.frequency,
              earliestPeriod:
                data.availablePeriods[data.availablePeriods.length - 1]?.id,
              latestPeriod: data.availablePeriods[0]?.id,
            }}
            availableConcepts={data.availableConcepts}
            onSuccess={handleMappingSuccess}
            onClose={() => setMappingEditor(null)}
            position={{ top: mappingEditor.top, left: mappingEditor.left }}
          />
        </>
      )}
    </div>
  );
};

// TODO: Refactor autocomplete to use new AutocompleteV2 component.
