import type { SalesOpportunity, OpportunityType } from "$lib/optima-api/modules/sales";
import type {
  CommittedQuote,
  WorkflowStatusResponse,
  WorkflowHistoryResponse,
  WorkflowStatusKey,
} from "$lib/optima-api/modules/sales";
import {
  WORKFLOW_STATUS_IDS,
  STATUS_ID_TO_KEY,
  WORKFLOW_STATUS_LABELS,
  TERMINAL_STATUSES,
  REOPENABLE_STATUSES,
} from "$lib/optima-api/modules/sales";
import type { PermissionMap } from "$lib/permissions";

export {
  WORKFLOW_STATUS_IDS,
  STATUS_ID_TO_KEY,
  WORKFLOW_STATUS_LABELS,
  TERMINAL_STATUSES,
  REOPENABLE_STATUSES,
};
export type { OpportunityType };
export type {
  WorkflowStatusResponse,
  WorkflowHistoryResponse,
  WorkflowStatusKey,
};

export interface OpportunityForecast {
  id: number;
  forecastType?: string;
  forecastMonth?: string;
  revenue?: number;
  cost?: number;
  forecastPercentage?: number;
  status?: { id: number; name: string };
  includedFlag?: boolean;
  linkedFlag?: boolean;
  recurringFlag?: boolean;
  [key: string]: unknown;
}

export interface NoteAuthor {
  id: string;
  identifier: string;
  name: string;
  cwMemberId?: number;
}

export interface OpportunityNote {
  id: number;
  text?: string;
  type?: { id: number; name: string };
  flagged?: boolean;
  enteredBy?: NoteAuthor | null;
  dateEntered?: string;
  _info?: { lastUpdated?: string; updatedBy?: string };
  [key: string]: unknown;
}

export function noteAuthorInitials(name?: string): string {
  if (!name) return "?";
  return name
    .split(/\s+/)
    .slice(0, 2)
    .map((w) => w[0]?.toUpperCase() ?? "")
    .join("");
}

export function formatDateTime(dateStr?: string | null): string {
  if (!dateStr) return "";
  try {
    const d = new Date(dateStr);
    return d.toLocaleDateString("en-US", {
      month: "short",
      day: "numeric",
      year: "numeric",
      hour: "numeric",
      minute: "2-digit",
    });
  } catch {
    return "";
  }
}

export interface OpportunityProduct {
  id: number;
  forecastDescription?: string;
  productDescription?: string;
  productClass?: string;
  forecastType?: string;
  quantity?: number;
  revenue?: number;
  cost?: number;
  margin?: number;
  profit?: number;
  percentage?: number;
  status?: { id: number; name: string };
  catalogItem?: { id: number; identifier: string };
  opportunity?: { id: number; name: string };
  includeFlag?: boolean;
  linkFlag?: boolean;
  recurringFlag?: boolean;
  taxableFlag?: boolean;
  cancelled?: boolean;
  cancellationType?: "full" | "partial" | null;
  quantityCancelled?: number;
  cancelledReason?: string | null;
  cancelledDate?: string | null;
  recurringRevenue?: number;
  recurringCost?: number;
  cycles?: number;
  sequenceNumber?: number;
  subNumber?: number;
  cwLastUpdated?: string;
  cwUpdatedBy?: string;
  onHand?: number | null;
  inStock?: boolean | null;
  productNarrative?: string | null;
  customerDescription?: string | null;
  procurementNotes?: string | null;
  unitPrice?: number;
  listPrice?: number;
  discount?: number;
  [key: string]: unknown;
}

export interface OpportunityContact {
  id: number;
  contact?: { id: number; name: string };
  company?: { id: number; identifier?: string; name: string };
  role?: { id: number; name: string };
  notes?: string;
  referralFlag?: boolean;
  [key: string]: unknown;
}

export interface PageData {
  opportunity: SalesOpportunity | null;
  opportunityId: string;
  notes: OpportunityNote[];
  contacts: OpportunityContact[];
  products: OpportunityProduct[];
  quotes: CommittedQuote[];
  accessToken: string | null;
  permissions: PermissionMap;
  workflowStatus: WorkflowStatusResponse | null;
  opportunityTypes: OpportunityType[];
}

export function opportunityInitials(name: string): string {
  return name
    .split(/\s+/)
    .slice(0, 2)
    .map((w) => w[0])
    .join("")
    .toUpperCase();
}

export function formatDate(dateStr?: string | null): string {
  if (!dateStr) return "—";
  try {
    // Strip time portion for date-only fields to prevent timezone shift
    const dateOnly = dateStr.split("T")[0];
    const date = new Date(dateOnly + "T00:00:00");
    if (isNaN(date.getTime())) return "—";
    return date.toLocaleDateString("en-US", {
      month: "short",
      day: "numeric",
      year: "numeric",
    });
  } catch {
    return "—";
  }
}

export function formatCurrency(amount?: number | null): string {
  if (amount == null) return "—";
  return new Intl.NumberFormat("en-US", {
    style: "currency",
    currency: "USD",
    minimumFractionDigits: 2,
  }).format(amount);
}

/**
 * Canonical status IDs → pipeline tier.
 * Equivalency IDs (legacy/pre-2024) are mapped to the same tier as their canonical parent.
 */
const STATUS_TIER: Record<number, string> = (() => {
  const map: Record<number, string> = {};
  // FutureLead (id 51) + equivalencies
  for (const id of [51, 35, 36]) map[id] = "status-future";
  // PendingNew (id 37)
  map[37] = "status-pending-new";
  // New (id 24) + equivalencies (37 removed — now its own tier)
  for (const id of [24, 1, 13]) map[id] = "status-new";
  // Internal Review (id 56) + equivalencies
  for (const id of [56, 10, 26, 27, 28, 41, 54]) map[id] = "status-review";
  // QuoteSent (id 43)
  map[43] = "status-quote-sent";
  // ConfirmedQuote (id 57)
  map[57] = "status-quote-confirmed";
  // ReadyToSend (id 63)
  map[63] = "status-ready-to-send";
  // PendingSent (id 60)
  map[60] = "status-pending-sent";
  // PendingRevision (id 61)
  map[61] = "status-pending-revision";
  // Active (id 58) + equivalencies (43, 57 removed — now own tiers)
  for (const id of [
    58, 9, 15, 16, 17, 18, 19, 20, 25, 38, 39, 40, 42, 44, 45, 46, 47, 48, 52,
    55,
  ])
    map[id] = "status-active";
  // PendingWon (id 49)
  map[49] = "status-pending-won";
  // Won (id 29) + equivalencies (49 removed — now own tier)
  for (const id of [29, 2]) map[id] = "status-won";
  // PendingLost (id 50)
  map[50] = "status-pending-lost";
  // Lost (id 53) + equivalencies (50 removed — now own tier)
  for (const id of [53, 3, 4, 12, 30, 31, 32, 33, 34]) map[id] = "status-lost";
  // Canceled (id 59)
  map[59] = "status-canceled";
  return map;
})();

/** Canonical display name for each tier */
const CANONICAL_NAMES: Record<number, string> = {
  51: "FutureLead",
  37: "Pending New",
  24: "New",
  56: "Internal Review",
  43: "Quote Sent",
  57: "Confirmed Quote",
  63: "Ready to Send",
  60: "Pending Sent",
  61: "Pending Revision",
  58: "Active",
  49: "Pending Won",
  29: "Won",
  50: "Pending Lost",
  53: "Lost",
  59: "Canceled",
};

/** IDs that are canonical (not equivalency-mapped) */
const CANONICAL_IDS = new Set([
  51, 37, 24, 56, 43, 57, 63, 60, 61, 58, 49, 29, 50, 53, 59,
]);

export function statusColorClass(opportunity: SalesOpportunity): string {
  if (opportunity.closedFlag) {
    const sid = opportunity.status?.id;
    if (sid != null && STATUS_TIER[sid]) return STATUS_TIER[sid];
    return "status-closed";
  }
  const sid = opportunity.status?.id;
  if (sid != null && STATUS_TIER[sid]) return STATUS_TIER[sid];
  return "status-open";
}

/** Get the canonical display label for the status (resolves equivalencies). */
export function statusLabel(opportunity: SalesOpportunity): string {
  if (opportunity.closedFlag) {
    const sid = opportunity.status?.id;
    if (sid != null) {
      for (const [canonId, name] of Object.entries(CANONICAL_NAMES)) {
        const tier = STATUS_TIER[sid];
        const canonTier = STATUS_TIER[Number(canonId)];
        if (tier && tier === canonTier) return name;
      }
    }
    return "Closed";
  }
  const sid = opportunity.status?.id;
  if (sid != null) {
    // If it IS the canonical ID, just use its name
    if (CANONICAL_IDS.has(sid))
      return CANONICAL_NAMES[sid] ?? opportunity.status?.name ?? "Open";
    // Otherwise it's an equivalency — return the canonical name
    const tier = STATUS_TIER[sid];
    if (tier) {
      for (const [canonId, name] of Object.entries(CANONICAL_NAMES)) {
        if (STATUS_TIER[Number(canonId)] === tier) return name;
      }
    }
  }
  return opportunity.status?.name ?? "Open";
}

/** Whether this status is an equivalency-mapped status (not canonical). */
export function isEquivalencyStatus(opportunity: SalesOpportunity): boolean {
  const sid = opportunity.status?.id;
  if (sid == null) return false;
  return STATUS_TIER[sid] != null && !CANONICAL_IDS.has(sid);
}

/** The original CW status name (for tooltip on equivalency statuses). */
export function originalStatusName(opportunity: SalesOpportunity): string {
  return opportunity.status?.name ?? "Unknown";
}
