import { createRoute } from "../../../../modules/api-utils/createRoute";
import { opportunities } from "../../../../managers/opportunities";
import { apiResponse } from "../../../../modules/api-utils/apiResponse";
import { ContentfulStatusCode } from "hono/utils/http-status";
import { authMiddleware } from "../../../middleware/authorization";
import GenericError from "../../../../Errors/GenericError";
import { z } from "zod";
import {
  OpportunityStatus,
  StatusIdToKey,
} from "../../../../workflows/wf.opportunity";
import { resolveCwProbabilityId } from "../../../../modules/cw-utils/opportunities/cwProbabilityCache";

const updateSchema = z
  .object({
    name: z.string().min(1).optional(),
    notes: z.string().optional(),
    interest: z.enum(["HOT", "WARM", "COLD"]).nullable().optional(),
    probability: z.number().min(0).max(100).optional(),
    rating: z.object({ id: z.number() }).optional(),
    type: z.object({ id: z.number() }).optional(),
    stage: z.object({ id: z.number() }).optional(),
    status: z.object({ id: z.number() }).optional(),
    priority: z.object({ id: z.number() }).optional(),
    campaign: z.object({ id: z.number() }).optional(),
    primarySalesRep: z.object({ id: z.number() }).optional(),
    secondarySalesRep: z.object({ id: z.number() }).nullable().optional(),
    company: z.object({ id: z.number() }).optional(),
    contact: z.object({ id: z.number() }).nullable().optional(),
    site: z.object({ id: z.number() }).nullable().optional(),
    expectedCloseDate: z
      .string()
      .optional()
      .transform((v) => (v ? new Date(v).toISOString() : v)),
    customerPO: z.string().nullable().optional(),
    source: z.string().nullable().optional(),
    locationId: z.number().optional(),
    businessUnitId: z.number().optional(),
  })
  .refine((d) => Object.values(d).some((v) => v !== undefined), {
    message: "At least one field must be provided",
  });

/* PATCH /v1/sales/opportunities/opportunity/:identifier */
export default createRoute(
  "patch",
  ["/opportunities/opportunity/:identifier"],
  async (c) => {
    const identifier = c.req.param("identifier");
    const body = await c.req.json();
    const data = updateSchema.parse(body);

    const item = await opportunities.fetchRecord(identifier);

    // Read-only guard: only New and Active statuses allow opportunity data mutations.
    const editableStatuses = new Set<number>([
      OpportunityStatus.New,
      OpportunityStatus.Active,
    ]);
    const currentStatusId = item.statusCwId ?? null;
    if (currentStatusId !== null && !editableStatuses.has(currentStatusId)) {
      const statusKey = StatusIdToKey[currentStatusId] ?? `ID ${currentStatusId}`;
      throw new GenericError({
        status: 422,
        name: "OpportunityReadOnly",
        message: `Opportunity data cannot be edited in "${statusKey}" status. Only "New" and "Active" opportunities are editable.`,
      });
    }

    try {
      const { probability: probabilityPercent, ...rest } = data;

      // Resolve numeric probability → CW reference ID
      let probabilityRef: { id: number } | undefined;
      if (probabilityPercent !== undefined) {
        const probId = await resolveCwProbabilityId(probabilityPercent);
        if (probId != null) probabilityRef = { id: probId };
      }

      const updated = await item.updateOpportunity({
        ...rest,
        ...(probabilityRef !== undefined ? { probability: probabilityRef } : {}),
      });

      const response = apiResponse.successful(
        "Opportunity updated successfully!",
        updated.toJson()
      );

      return c.json(response, response.status as ContentfulStatusCode);
    } catch (err) {
      const isAxios =
        err != null && typeof err === "object" && "isAxiosError" in err;

      if (isAxios) {
        const axiosErr = err as any;
        const cwStatus: number = axiosErr.response?.status ?? 502;
        const cwData = axiosErr.response?.data;
        const cwMessage: string =
          cwData?.message ?? "Failed to update the opportunity in ConnectWise";
        const cwErrors: unknown[] | undefined = Array.isArray(cwData?.errors)
          ? cwData.errors
          : undefined;

        return c.json(
          {
            status: cwStatus,
            message: cwMessage,
            error: "ConnectWiseUpdateError",
            successful: false,
            errors: cwErrors,
            meta: { timestamp: Date.now() },
          },
          cwStatus as ContentfulStatusCode
        );
      }

      throw new GenericError({
        status: 500,
        name: "OpportunitySaveError",
        message: "Failed to save opportunity data",
        cause: err instanceof Error ? err.message : String(err),
      });
    }
  },
  authMiddleware({ permissions: ["sales.opportunity.update"] })
);
