import api from "../axios";

export interface SalesOpportunity {
  id: string;
  cwOpportunityId?: number;
  name: string;
  description?: string | null;
  notes?: string | null;
  type?: {
    id?: number;
    name?: string;
    wonFlag?: boolean;
    lostFlag?: boolean;
    closedFlag?: boolean;
  } | null;
  stage?: { id?: number; name?: string } | null;
  status?: { id?: number; name?: string } | null;
  priority?: { id?: number; name?: string } | null;
  interest?: "HOT" | "WARM" | "COLD" | null;
  source?: string | null;
  campaign?: string | null;
  primarySalesRep?: {
    id?: number;
    identifier?: string;
    name?: string;
  } | null;
  secondarySalesRep?: {
    id?: number;
    identifier?: string;
    name?: string;
  } | null;
  company?: {
    id?: string;
    name?: string;
    cw_Identifier?: string;
    cw_CompanyId?: number;
    cw_Data?: {
      address?: {
        line1?: string;
        line2?: string | null;
        city?: string;
        state?: string;
        zip?: string;
        country?: string;
      };
      allContacts?: {
        firstName?: string;
        lastName?: string;
        cwId?: number;
        inactive?: boolean;
        title?: string;
        phone?: string;
        email?: string;
      }[];
    };
  } | null;
  contact?: { id?: number | string; name?: string } | null;
  site?: {
    id?: number | string;
    name?: string;
    address?: {
      line1?: string;
      line2?: string | null;
      city?: string;
      state?: string;
      zip?: string;
      country?: string;
    };
    phoneNumber?: string | null;
    faxNumber?: string | null;
    primaryAddressFlag?: boolean;
    defaultShippingFlag?: boolean;
    defaultBillingFlag?: boolean;
    defaultMailingFlag?: boolean;
  } | null;
  customerPO?: string | null;
  totalSalesTax?: number | null;
  expectedSalesTaxRate?: number | null;
  taxCodeDescription?: string | null;
  location?: { id?: number; name?: string } | null;
  department?: { id?: number; name?: string } | null;
  expectedCloseDate?: string | null;
  pipelineChangeDate?: string | null;
  dateBecameLead?: string | null;
  closedDate?: string | null;
  closedFlag?: boolean;
  probability?: { id?: number; percent?: number } | null;
  closedBy?:
  | string
  | { id?: number | string; identifier?: string; name?: string }
  | null;
  companyId?: string;
  productSequence?: number[] | null;
  cwLastUpdated?: string | null;
  createdAt?: string;
  updatedAt?: string;
  activities?: OpportunityActivity[];
}

// ── Workflow Types ──────────────────────────────────────────────────

export type WorkflowAction =
  | "acceptNew"
  | "requestReview"
  | "reviewDecision"
  | "sendQuote"
  | "ReadyToSend"
  | "confirmQuote"
  | "finalize"
  | "resurrect"
  | "beginRevision"
  | "resendQuote"
  | "cancel"
  | "reopen"
  | "sendBackForRevision"
  | "createScheduleEntry";

export type ReviewDecision = "approve" | "reject" | "send" | "cancel";

export interface WorkflowActionPayload {
  note?: string;
  timeStarted?: string;
  timeEnded?: string;
  // sendQuote / resendQuote compound flags
  quoteConfirmed?: boolean;
  won?: boolean;
  lost?: boolean;
  needsRevision?: boolean;
  // reviewDecision
  decision?: ReviewDecision;
  // finalize
  outcome?: "won" | "lost";
  finalize?: boolean;
  // createScheduleEntry
  activityTypeValue?: "Follow-Up" | "Appointment" | "Admin";
  dueDate?: string;
  startTime?: string;
  endTime?: string;
}

export interface WorkflowAvailableAction {
  action: WorkflowAction;
  label: string;
  targetStatuses: { key: string; id: number }[];
  requiresNote: boolean;
  requiresPermission: string | null;
  permitted: boolean;
  payloadHints?: Record<string, string>;
}

export interface WorkflowStatusResponse {
  currentStatusId: number;
  currentStatus: string;
  stageName: string;
  isOptimaStage: boolean;
  isTerminal: boolean;
  availableActions: WorkflowAvailableAction[];
  coldCheck: ColdCheckResult | null;
}

export interface ColdCheckResult {
  isCold: boolean;
  daysSinceActivity?: number;
  threshold?: number;
  status?: string;
}

export interface WorkflowResult {
  previousStatusId: number;
  previousStatus: string;
  newStatusId: number | null;
  newStatus: string | null;
  activitiesCreated: OpportunityActivity[];
  coldCheck: ColdCheckResult | null;
}

export interface OpportunityActivity {
  cwActivityId?: number;
  name?: string;
  notes?: string;
  type?: { id?: number; name?: string };
  status?: { id?: number; name?: string };
  company?: { id?: number; identifier?: string; name?: string };
  contact?: { id?: number; name?: string };
  phoneNumber?: string;
  email?: string;
  opportunity?: { id?: number; name?: string };
  ticket?: unknown;
  agreement?: unknown;
  campaign?: unknown;
  assignTo?: { id?: number; identifier?: string; name?: string };
  scheduleStatus?: unknown;
  reminder?: unknown;
  where?: string | null;
  dateStart?: string;
  dateEnd?: string;
  notifyFlag?: boolean;
  currency?: unknown;
  mobileGuid?: string | null;
  customFields?: { id?: number; caption?: string; value?: string }[];
  cwLastUpdated?: string;
  cwDateEntered?: string;
  cwEnteredBy?: string;
  cwUpdatedBy?: string;
  closed?: boolean;
  closedAt?: string;
}

export interface WorkflowTimeEntry {
  id: string;
  cwId: number;
  memberId?: string | null;
  member?: { name: string; email: string | null } | null;
  dateStart?: string | null;
  timeStart?: string | null;
  timeEnd?: string | null;
  notes?: string | null;
  actualHours?: number | null;
  billableHours?: number | null;
  billableFlag?: boolean | null;
}

export interface WorkflowHistoryEntry {
  activity: OpportunityActivity;
  optimaType: string;
  quoteId?: string | null;
  parentActivityId?: number | null;
  closed?: boolean;
  closedAt?: string | null;
  timeEntries?: WorkflowTimeEntry[];
}

export interface WorkflowHistoryResponse {
  opportunityId: string;
  cwOpportunityId: number;
  totalActivities: number;
  activities: WorkflowHistoryEntry[];
}

// ── Workflow Status IDs ─────────────────────────────────────────────

export const WORKFLOW_STATUS_IDS = {
  PendingNew: 37,
  New: 24,
  InternalReview: 56,
  QuoteSent: 43,
  ConfirmedQuote: 57,
  ReadyToSend: 63,
  Active: 58,
  PendingSent: 60,
  PendingRevision: 61,
  PendingWon: 49,
  Won: 29,
  PendingLost: 50,
  Lost: 53,
  Canceled: 59,
} as const;

export type WorkflowStatusKey = keyof typeof WORKFLOW_STATUS_IDS;

/** Reverse lookup: CW status ID → workflow status key */
export const STATUS_ID_TO_KEY: Record<number, WorkflowStatusKey> =
  Object.fromEntries(
    Object.entries(WORKFLOW_STATUS_IDS).map(([k, v]) => [
      v,
      k as WorkflowStatusKey,
    ])
  ) as Record<number, WorkflowStatusKey>;

/** Human-readable labels for each workflow status */
export const WORKFLOW_STATUS_LABELS: Record<WorkflowStatusKey, string> = {
  PendingNew: "Pending New",
  New: "New",
  InternalReview: "Internal Review",
  QuoteSent: "Quote Sent",
  ConfirmedQuote: "Confirmed Quote",
  ReadyToSend: "Ready to Send",
  Active: "Active",
  PendingSent: "Pending Sent",
  PendingRevision: "Pending Revision",
  PendingWon: "Pending Won",
  Won: "Won",
  PendingLost: "Pending Lost",
  Lost: "Lost",
  Canceled: "Canceled",
};

/** Terminal statuses — no workflow actions allowed */
export const TERMINAL_STATUSES: ReadonlySet<WorkflowStatusKey> = new Set([
  "Won",
  "Lost",
]);

/** Statuses that can be re-opened */
export const REOPENABLE_STATUSES: ReadonlySet<WorkflowStatusKey> = new Set([
  "Canceled",
]);

/** Statuses where opportunity data is fully editable (frontend + backend). */
export const EDITABLE_STATUSES: ReadonlySet<WorkflowStatusKey> = new Set([
  "New",
  "Active",
]);

/**
 * Statuses where the "Add Time" button is hidden.
 * PendingWon, PendingLost, Won, Lost, Canceled.
 */
export const ADD_TIME_EXCLUDED_STATUSES: ReadonlySet<WorkflowStatusKey> = new Set([
  "PendingWon",
  "PendingLost",
  "Won",
  "Lost",
  "Canceled",
]);

/** Statuses where the quote has been confirmed (finalize is allowed) */
export const QUOTE_CONFIRMED_STATUSES: ReadonlySet<WorkflowStatusKey> = new Set(
  ["ConfirmedQuote", "ReadyToSend", "Active", "PendingWon", "PendingLost"]
);

export interface OpportunityType {
  id: number;
  name: string;
  wonFlag?: boolean;
  lostFlag?: boolean;
  closedFlag?: boolean;
  inactiveFlag?: boolean;
  defaultFlag?: boolean;
  enteredBy?: string;
  dateEntered?: string;
  _info?: { lastUpdated?: string; updatedBy?: string };
  connectWiseId?: string;
  optimaEquivalency?: number[];
}

export interface AddProductBody {
  catalogItem?: { id: number };
  forecastDescription?: string;
  productDescription?: string;
  customerDescription?: string;
  procurementNotes?: string;
  productNarrative?: string;
  quantity?: number;
  status?: { id: number };
  productClass?: string;
  forecastType?: string;
  revenue?: number;
  cost?: number;
  includeFlag?: boolean;
  linkFlag?: boolean;
  recurringFlag?: boolean;
  taxableFlag?: boolean;
  recurringRevenue?: number;
  recurringCost?: number;
  cycles?: number;
  sequenceNumber?: number;
}

export interface SpecialOrderBody {
  desc: string;
  customerDesc?: string;
  qty?: number;
  price: number;
  cost?: number;
  taxable?: boolean;
  procurementNotes?: string;
  productNarrative?: string;
}

export type LaborStyle = "field" | "tech";
export type LaborCustomerType = "corporate" | "residential";

export interface LaborOptionsResponse {
  defaults: {
    customerType: LaborCustomerType;
    rates: Record<LaborCustomerType, number>;
    cpuMultiplier: number;
    quantity: number;
  };
  options: Record<
    LaborStyle,
    {
      cwCatalogId: number;
      identifier: string;
      name: string;
      taxableFlag?: boolean;
    }
  >;
}

export interface AddLaborBody {
  laborStyle: LaborStyle;
  customerType?: LaborCustomerType;
  hours?: number;
  taxable?: boolean;
  taxableFlag?: boolean;
  rate?: number;
  ppu?: number;
  cpu?: number;
  description?: string;
  customerDescription?: string;
  procurementNotes?: string;
  productNarrative?: string;
}

export interface EditOpportunityProductBody {
  productDescription?: string;
  quantity?: number;
  unitPrice?: number;
  unitCost?: number;
  customerDescription?: string | null;
  productNarrative?: string | null;
  procurementNotes?: string | null;
  taxableFlag?: boolean;
}

export interface CancelOpportunityProductBody {
  quantityCancelled: number;
  cancellationReason?: string | null;
}

export interface CreateOpportunityBody {
  name: string;
  expectedCloseDate: string;
  notes?: string;
  interest?: "HOT" | "WARM" | "COLD" | null;
  type?: { id: number };
  stage?: { id: number };
  status?: { id: number };
  priority?: { id: number };
  campaign?: { id: number };
  primarySalesRep?: { id: number };
  secondarySalesRep?: { id: number } | null;
  company?: { id: number };
  contact?: { id: number } | null;
  site?: { id: number } | null;
  source?: string | null;
  customerPO?: string | null;
  locationId?: number;
  businessUnitId?: number;
}

export interface QuoteRegenProduct {
  cwForecastId?: number;
  forecastDescription?: string;
  productDescription?: string;
  customerDescription?: string;
  productNarrative?: string;
  productClass?: string;
  forecastType?: string;
  catalogItem?: { id?: number; identifier?: string };
  quantity?: number;
  effectiveQuantity?: number;
  revenue?: number;
  cost?: number;
  margin?: number;
  percentage?: number;
  includeFlag?: boolean;
  taxableFlag?: boolean;
  recurringFlag?: boolean;
  recurringRevenue?: number;
  recurringCost?: number;
  sequenceNumber?: number;
  cancelledFlag?: boolean;
  cancellationType?: string | null;
  quantityCancelled?: number;
  cancelledReason?: string | null;
  cancelledDate?: string | null;
}

export interface QuoteDownloadRecord {
  downloadedAt: string;
  fetchAction: "download" | "print";
  userId: string;
  userName: string;
  userEmail: string;
}

export interface QuoteDownloadLog {
  quoteId: string;
  quoteFileName: string;
  createdById: string;
  createdAt: string;
  downloads: QuoteDownloadRecord[];
}

export interface CommittedQuote {
  id: string;
  quoteFileName: string;
  quoteRegenHash?: string;
  opportunityId: string;
  createdById: string;
  quoteRegenData?: {
    options?: {
      lineItemPricing?: boolean;
      includeQuoteNarrative?: boolean;
      includeItemNarratives?: boolean;
      showPreview?: boolean;
    };
    opportunity?: {
      id?: string;
      cwOpportunityId?: number;
      name?: string;
      totalSalesTax?: number;
      contactName?: string;
      companyName?: string;
    };
    customer?: {
      preparedFor?: string;
      companyName?: string;
      primaryContact?: {
        firstName?: string;
        lastName?: string;
        email?: string;
        phone?: string;
      };
      siteAddress?: string[];
      companyAddress?: string[];
    };
    salesRep?: {
      name?: string;
      email?: string;
    };
    quoteNarrative?: string;
    products?: QuoteRegenProduct[];
    snapshotTimestamp?: string;
  } | null;
  quoteRegenParams?: {
    opportunityId?: string;
    cwOpportunityId?: number;
  } | null;
  createdAt: string;
  updatedAt: string;
}

export interface MemberSalesMetrics {
  memberIdentifier: string;
  memberName: string;
  generatedAt: string;
  pipelineRevenue: number;
  closedWonRevenueMtd: number;
  closedWonRevenueYtd: number;
  winCount: { mtd: number; ytd: number };
  lossCount: { mtd: number; ytd: number };
  avgDaysToClose: number;
  openOpportunityCount: number;
  wonOpportunityCount: { mtd: number; ytd: number };
  lostOpportunityCount: { mtd: number; ytd: number };
  closedOpportunityCount: { mtd: number; ytd: number };
  weightedPipelineRevenue: number;
  taxablePipelineRevenue: number;
  nonTaxablePipelineRevenue: number;
  avgOpenDealSize: number;
  avgWonDealSize: { mtd: number; ytd: number };
  winRate: { mtd: number; ytd: number };
  lossRate: { mtd: number; ytd: number };
  assignedOpportunityCount: number;
}

export const sales = {
  async fetchMetrics(accessToken: string): Promise<MemberSalesMetrics | null> {
    const response = await api.get("/v1/sales/opportunities/metrics", {
      headers: {
        Authorization: `Bearer ${accessToken}`,
      },
    });
    return response.data?.data ?? null;
  },

  async fetchMe(accessToken: string, includeClosed: boolean = false) {
    const params: Record<string, unknown> = {};
    if (includeClosed) params.includeClosed = true;
    const response = await api.get("/v1/sales/opportunities/@me", {
      params,
      headers: {
        Authorization: `Bearer ${accessToken}`,
      },
    });
    return response.data;
  },

  async fetchMany(
    accessToken: string,
    page: number = 1,
    search: string = "",
    rpp: number = 30,
    includeClosed: boolean = true
  ) {
    const params: Record<string, unknown> = { page, rpp };
    if (search) params.search = search;
    if (includeClosed) params.includeClosed = true;

    const response = await api.get("/v1/sales/opportunities", {
      params,
      headers: {
        Authorization: `Bearer ${accessToken}`,
      },
    });
    return response.data;
  },

  async fetchOpportunityTypes(accessToken: string) {
    const response = await api.get("/v1/sales/opportunity-types", {
      headers: {
        Authorization: `Bearer ${accessToken}`,
      },
    });
    return response.data;
  },

  async createOpportunity(accessToken: string, body: CreateOpportunityBody) {
    const response = await api.post("/v1/sales/opportunities", body, {
      headers: {
        Authorization: `Bearer ${accessToken}`,
      },
    });
    return response.data;
  },

  async fetchOne(
    accessToken: string,
    identifier: string,
    include?: ("notes" | "contacts" | "products" | "quotes")[]
  ) {
    const params: Record<string, string> = {};
    if (include && include.length > 0) {
      params.include = include.join(",");
    }
    const response = await api.get(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}`,
      {
        params,
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data;
  },

  async fetchForecasts(accessToken: string, identifier: string) {
    const response = await api.get(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/forecasts`,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data;
  },

  async fetchProducts(accessToken: string, identifier: string) {

    console.log("fetch prod exec check")

    const response = await api.get(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/products`,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );

    console.log("[fetchProducts] API response:", JSON.stringify(response.data, null, 2));

    return response.data;
  },

  async fetchNotes(accessToken: string, identifier: string) {
    const response = await api.get(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/notes`,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data;
  },

  async createNote(
    accessToken: string,
    identifier: string,
    data: { text: string; flagged?: boolean }
  ) {
    const response = await api.post(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/notes`,
      data,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data;
  },

  async updateNote(
    accessToken: string,
    identifier: string,
    noteId: number,
    data: { text?: string; flagged?: boolean }
  ) {
    const response = await api.patch(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/notes/${noteId}`,
      data,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data;
  },

  async deleteNote(accessToken: string, identifier: string, noteId: number) {
    const response = await api.delete(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/notes/${noteId}`,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data;
  },

  async fetchContacts(accessToken: string, identifier: string) {
    const response = await api.get(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/contacts`,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data;
  },

  async sequenceProducts(
    accessToken: string,
    identifier: string,
    orderedIds: number[]
  ) {
    const response = await api.patch(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/products/sequence`,
      { orderedIds },
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data;
  },

  async addProduct(
    accessToken: string,
    identifier: string,
    body: AddProductBody
  ) {
    const response = await api.post(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/products`,
      body,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data;
  },

  async addSpecialOrder(
    accessToken: string,
    identifier: string,
    body: SpecialOrderBody | SpecialOrderBody[]
  ) {
    const response = await api.post(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/products/special-order`,
      body,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data;
  },

  async fetchLaborOptions(accessToken: string, identifier: string) {
    const response = await api.get(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/products/labor/options`,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data as {
      status?: number;
      message?: string;
      data: LaborOptionsResponse;
      successful?: boolean;
    };
  },

  async addLabor(accessToken: string, identifier: string, body: AddLaborBody) {
    const response = await api.post(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/products/labor`,
      body,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data;
  },

  async editProduct(
    accessToken: string,
    identifier: string,
    productId: number,
    body: EditOpportunityProductBody
  ) {
    const response = await api.patch(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/products/${productId}/edit`,
      body,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data;
  },

  async cancelProduct(
    accessToken: string,
    identifier: string,
    productId: number,
    body: CancelOpportunityProductBody
  ) {
    const response = await api.patch(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/products/${productId}/cancel`,
      body,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data;
  },

  async refreshOpportunity(accessToken: string, identifier: string) {
    const response = await api.post(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/refresh`,
      {},
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data;
  },

  async updateOpportunity(
    accessToken: string,
    identifier: string,
    body: Record<string, unknown>
  ) {
    const response = await api.patch(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}`,
      body,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data;
  },

  async fetchQuotes(
    accessToken: string,
    identifier: string,
    options?: {
      includeRegenData?: boolean;
      includeRegenParams?: boolean;
    }
  ) {
    const params: Record<string, string> = {};
    if (options?.includeRegenData) params.includeRegenData = "true";
    if (options?.includeRegenParams) params.includeRegenParams = "true";
    const response = await api.get(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/quotes`,
      {
        params,
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data as {
      status?: number;
      message?: string;
      data: CommittedQuote[];
      successful?: boolean;
    };
  },

  async commitQuote(
    accessToken: string,
    identifier: string,
    body?: {
      lineItemPricing?: boolean;
      includeQuoteNarrative?: boolean;
      includeItemNarratives?: boolean;
      separateRecurringServices?: boolean;
    }
  ) {
    const response = await api.post(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/quote/commit`,
      body ?? {},
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data as {
      status?: number;
      message?: string;
      data: {
        id: string;
        quoteFileName: string;
        opportunityId: string;
        createdById: string;
        quoteRegenData: {
          lineItemPricing: boolean;
          includeQuoteNarrative: boolean;
          includeItemNarratives: boolean;
          showPreview: boolean;
        };
        createdAt: string;
        updatedAt: string;
      };
      successful?: boolean;
    };
  },

  async fetchQuoteNarrative(accessToken: string, identifier: string) {
    const response = await api.get(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/quotes/narrative`,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data as {
      status?: number;
      message?: string;
      data: {
        quoteNarrative: string | null;
      };
      successful?: boolean;
    };
  },

  async updateQuoteNarrative(
    accessToken: string,
    identifier: string,
    quoteNarrative: string | null
  ) {
    const response = await api.patch(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/quotes/narrative`,
      {
        quoteNarrative,
      },
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data as {
      status?: number;
      message?: string;
      data: {
        quoteNarrative: string | null;
      };
      successful?: boolean;
    };
  },

  async previewQuote(accessToken: string, identifier: string, quoteId: string) {
    const response = await api.get(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/quote/${encodeURIComponent(quoteId)}/preview`,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data as {
      status?: number;
      message?: string;
      data: {
        mimeType: string;
        contentBase64: string;
      };
      successful?: boolean;
    };
  },

  async downloadQuote(
    accessToken: string,
    identifier: string,
    quoteId: string,
    fetchAction: "download" | "print"
  ) {
    const response = await api.get(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/quote/${encodeURIComponent(quoteId)}/download`,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
        params: { fetchAction },
      }
    );
    return response.data as {
      status?: number;
      message?: string;
      data: {
        id: string;
        quoteFileName: string;
        mimeType: string;
        contentBase64: string;
      };
      successful?: boolean;
    };
  },

  async fetchQuoteDownloads(accessToken: string, identifier: string) {
    const response = await api.get(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/quotes/downloads`,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data as {
      status?: number;
      message?: string;
      data: QuoteDownloadLog[];
      successful?: boolean;
    };
  },

  async deleteProduct(
    accessToken: string,
    identifier: string,
    productId: number
  ) {
    const response = await api.delete(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/products/${productId}`,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data;
  },

  async deleteOpportunity(accessToken: string, identifier: string) {
    const response = await api.delete(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}`,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data;
  },

  // ── Workflow Methods ──────────────────────────────────────────────

  async fetchWorkflowStatus(accessToken: string, identifier: string) {
    const response = await api.get(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/workflow`,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data as {
      status?: number;
      message?: string;
      data: WorkflowStatusResponse;
      successful?: boolean;
    };
  },

  async dispatchWorkflowAction(
    accessToken: string,
    identifier: string,
    action: WorkflowAction,
    payload: WorkflowActionPayload
  ) {
    const response = await api.post(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/workflow`,
      { action, payload },
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data as {
      status?: number;
      message?: string;
      data: WorkflowResult;
      successful?: boolean;
    };
  },

  async fetchWorkflowHistory(
    accessToken: string,
    identifier: string,
    type?: string
  ) {
    const params: Record<string, string> = {};
    if (type) params.type = type;
    const response = await api.get(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(
        identifier
      )}/workflow/history`,
      {
        params,
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );
    return response.data as {
      status?: number;
      message?: string;
      data: WorkflowHistoryResponse;
      successful?: boolean;
    };
  },

  async fetchOpenActivities(accessToken: string, identifier: string) {
    const response = await api.get(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/activities`,
      { headers: { Authorization: `Bearer ${accessToken}` } }
    );
    return response.data as {
      successful?: boolean;
      data: {
        activities: Array<{
          cwActivityId: number;
          name: string;
          optimaType: string | null;
          parentActivityId: number | null;
          status: { id?: number; name?: string } | null;
          dateStart: string | null;
          dateEnd: string | null;
        }>;
      };
    };
  },

  async addTimeToActivity(
    accessToken: string,
    identifier: string,
    payload: { activityId: number; timeStarted: string; timeEnded: string; notes?: string }
  ) {
    const response = await api.post(
      `/v1/sales/opportunities/opportunity/${encodeURIComponent(identifier)}/time`,
      payload,
      { headers: { Authorization: `Bearer ${accessToken}` } }
    );
    return response.data as {
      successful?: boolean;
      message?: string;
      data: { cwTimeEntryId: number | null; activityId: number; activityWasClosed: boolean };
    };
  },
};
