import { z } from "zod";
import { createRoute } from "../../../../modules/api-utils/createRoute";
import { apiResponse } from "../../../../modules/api-utils/apiResponse";
import { ContentfulStatusCode } from "hono/utils/http-status";
import { authMiddleware } from "../../../middleware/authorization";
import { opportunities } from "../../../../managers/opportunities";
import { cwMembers } from "../../../../managers/cwMembers";
import { activityCw } from "../../../../modules/cw-utils/activities/activities";
import { submitTimeEntry } from "../../../../services/cw.opportunityService";
import { OptimaType } from "../../../../workflows/wf.opportunity";
import GenericError from "../../../../Errors/GenericError";

const addTimeSchema = z.object({
  /** CW activity ID to log time against. */
  activityId: z.number().int().positive(),
  /** ISO-8601 datetime when work started. */
  timeStarted: z.string().datetime(),
  /** ISO-8601 datetime when work ended. */
  timeEnded: z.string().datetime(),
  /** Optional notes for the time entry. */
  notes: z.string().optional(),
});

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

    const user = c.get("user");

    if (!user.cwIdentifier) {
      throw new GenericError({
        status: 400,
        name: "MissingCwIdentifier",
        message:
          "Your account is not linked to a ConnectWise member. A CW member association is required to log time.",
      });
    }

    // Verify the opportunity exists and belongs to the correct identifier
    const opportunity = await opportunities.fetchItem(identifier);

    const cwMember = await cwMembers.fetch(user.cwIdentifier);

    // Fetch the activity to verify it belongs to this opportunity and is open
    const activity = await activityCw.fetch(data.activityId);

    if (activity.opportunity?.id !== opportunity.cwOpportunityId) {
      throw new GenericError({
        status: 400,
        name: "ActivityMismatch",
        message: "The specified activity does not belong to this opportunity.",
      });
    }

    if (activity.status?.id === 2) {
      throw new GenericError({
        status: 400,
        name: "ActivityClosed",
        message: "Cannot log time against a closed activity.",
      });
    }

    // Submit the time entry
    const result = await submitTimeEntry({
      activityId: data.activityId,
      cwMemberId: cwMember.cwMemberId,
      timeStart: data.timeStarted,
      timeEnd: data.timeEnded,
      notes: data.notes ?? "",
    });

    if (!result.success) {
      throw new GenericError({
        status: 502,
        name: "TimeEntryFailed",
        message: result.message,
      });
    }

    // If the activity is a Schedule Entry (Optima_Type = "Schedule Entry"),
    // close it now that time has been logged against it.
    const optimaTypeField = activity.customFields?.find(
      (f: any) => f.id === OptimaType.FIELD_ID || f.caption === "Optima_Type",
    );
    if (optimaTypeField?.value === OptimaType.ScheduleEntry) {
      try {
        await activityCw.update(data.activityId, [
          { op: "replace", path: "status", value: { id: 2 } },
        ]);
      } catch (closeErr) {
        // Non-fatal — time entry was already submitted successfully
        console.error(
          `[AddTime] Failed to close Schedule Entry activity ${data.activityId}:`,
          closeErr,
        );
      }
    }

    const response = apiResponse.successful("Time entry submitted successfully.", {
      cwTimeEntryId: result.cwTimeEntryId,
      activityId: data.activityId,
      activityWasClosed: optimaTypeField?.value === OptimaType.ScheduleEntry,
    });
    return c.json(response, response.status as ContentfulStatusCode);
  },
  authMiddleware({ permissions: ["sales.opportunity.update"] }),
);
