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 { timeEntries } from "../../../../../managers/timeEntries";
import { activityCw } from "../../../../../modules/cw-utils/activities/activities";
import { ActivityController } from "../../../../../controllers/ActivityController";
import { OptimaType } from "../../../../../workflows/wf.opportunity";
import { prisma } from "../../../../../constants";

// ═══════════════════════════════════════════════════════════════════════════
// HELPERS
// ═══════════════════════════════════════════════════════════════════════════

const OPTIMA_TYPE_VALUES = new Set<string>([
  OptimaType.OpportunityCreated,
  OptimaType.OpportunitySetup,
  OptimaType.OpportunityReview,
  OptimaType.QuoteSent,
  OptimaType.QuoteConfirmed,
  OptimaType.QuoteSentConfirmed,
  OptimaType.QuoteGenerated,
  OptimaType.Revision,
  OptimaType.Finalized,
  OptimaType.Converted,
  OptimaType.ScheduleEntry,
]);

/** QuoteID custom field ID (matches wf.opportunity.ts QUOTE_ID_FIELD_ID). */
const QUOTE_ID_FIELD_ID = 48;

/** Close Date custom field ID (matches wf.opportunity.ts CLOSE_DATE_FIELD_ID). */
const CLOSE_DATE_FIELD_ID = 49;

/** Parent_Activity custom field ID. */
const PARENT_ACTIVITY_FIELD_ID = 50;

/**
 * Extract the Optima_Type value from a CW activity's custom fields.
 * Returns the string value if present, or null.
 */
function extractOptimaType(
  customFields: { id: number; value: unknown }[] | undefined,
): string | null {
  if (!customFields) return null;
  const field = customFields.find((f) => f.id === OptimaType.FIELD_ID);
  if (!field?.value || typeof field.value !== "string") return null;
  return OPTIMA_TYPE_VALUES.has(field.value) ? field.value : null;
}

/**
 * Extract the QuoteID custom field value from a CW activity.
 * Returns the string value or null.
 */
function extractQuoteId(
  customFields: { id: number; value: unknown }[] | undefined,
): string | null {
  if (!customFields) return null;
  const field = customFields.find((f) => f.id === QUOTE_ID_FIELD_ID);
  if (!field?.value || typeof field.value !== "string") return null;
  return field.value;
}

/**
 * Extract the Close Date custom field value from a CW activity.
 * Returns the ISO-8601 string or null.
 */
function extractCloseDate(
  customFields: { id: number; value: unknown }[] | undefined,
): string | null {
  if (!customFields) return null;
  const field = customFields.find((f) => f.id === CLOSE_DATE_FIELD_ID);
  if (!field?.value || typeof field.value !== "string") return null;
  return field.value;
}

/**
 * Extract the Parent_Activity custom field value from a CW activity.
 * Returns the numeric activity ID or null.
 */
function extractParentActivityId(
  customFields: { id: number; value: unknown }[] | undefined,
): number | null {
  if (!customFields) return null;
  const field = customFields.find(
    (f) => f.id === PARENT_ACTIVITY_FIELD_ID || (f as any).caption === "Parent_Activity",
  );
  if (!field?.value) return null;
  const parsed = parseInt(String(field.value), 10);
  return isNaN(parsed) ? null : parsed;
}

// ═══════════════════════════════════════════════════════════════════════════
// ROUTE
// ═══════════════════════════════════════════════════════════════════════════

/* GET /v1/sales/opportunities/opportunity/:identifier/workflow/history */
export default createRoute(
  "get",
  ["/opportunities/opportunity/:identifier/workflow/history"],
  async (c) => {
    try {
      const identifier = c.req.param("identifier");
      const filterType = c.req.query("type") ?? null; // optional filter by Optima_Type value

      // Resolve the opportunity to get the CW opportunity ID
      const opportunity = await opportunities.fetchItem(identifier);

      // Fetch all activities for this opportunity from CW
      const activitiesCollection = await activityCw.fetchByOpportunity(
        opportunity.cwOpportunityId,
      );

      // Filter to workflow activities (those with a valid Optima_Type)
      const workflowActivities: {
        activity: ReturnType<ActivityController["toJson"]>;
        optimaType: string;
        quoteId: string | null;
        parentActivityId: number | null;
        closed: boolean;
        closedAt: string | null;
      }[] = [];

      for (const [, raw] of activitiesCollection) {
        const controller = new ActivityController(raw);
        const json = controller.toJson();
        const optimaType = extractOptimaType(raw.customFields);

        if (!optimaType) continue;
        if (filterType && optimaType !== filterType) continue;

        const quoteId = extractQuoteId(raw.customFields);
        const parentActivityId = extractParentActivityId(raw.customFields);
        const closed = raw.status?.id === 2;
        const closedAt = extractCloseDate(raw.customFields);

        workflowActivities.push({
          activity: json,
          optimaType,
          quoteId,
          parentActivityId,
          closed,
          closedAt,
        });
      }

      // Sort newest first
      workflowActivities.sort((a, b) => {
        const dateA = new Date(
          a.activity.dateEnd ?? a.activity.dateStart ?? 0,
        ).getTime();
        const dateB = new Date(
          b.activity.dateEnd ?? b.activity.dateStart ?? 0,
        ).getTime();
        return dateB - dateA;
      });

      // Attach time entries for each activity in parallel
      const activitiesWithTimeEntries = await Promise.all(
        workflowActivities.map(async (item) => {
          const entries = await timeEntries.fetchByActivity(
            item.activity.cwActivityId,
          );
          return {
            ...item,
            timeEntries: entries.map((e) => e.toJson()),
          };
        }),
      );

      // Resolve CwMember info for all unique memberIds across time entries
      const allMemberIds = Array.from(
        new Set(
          activitiesWithTimeEntries.flatMap((item) =>
            item.timeEntries
              .map((te) => te.memberId)
              .filter((id): id is string => !!id),
          ),
        ),
      );

      const memberRecords = allMemberIds.length
        ? await prisma.cwMember.findMany({
          where: { identifier: { in: allMemberIds } },
          select: { identifier: true, firstName: true, lastName: true, officeEmail: true },
        })
        : [];

      const memberMap = new Map(
        memberRecords.map((m) => [
          m.identifier,
          { name: `${m.firstName} ${m.lastName}`.trim(), email: m.officeEmail ?? null },
        ]),
      );

      // Attach member info to each time entry
      const enrichedActivities = activitiesWithTimeEntries.map((item) => ({
        ...item,
        timeEntries: item.timeEntries.map((te) => ({
          ...te,
          member: te.memberId ? (memberMap.get(te.memberId) ?? null) : null,
        })),
      }));

      const response = apiResponse.successful(
        "Workflow history fetched successfully.",
        {
          opportunityId: opportunity.id,
          cwOpportunityId: opportunity.cwOpportunityId,
          totalActivities: enrichedActivities.length,
          activities: enrichedActivities,
        },
      );
      return c.json(response, response.status as ContentfulStatusCode);
    } catch (err) {
      console.error("[Workflow History] Unhandled error:", err);
      throw err;
    }
  },
  authMiddleware({ permissions: ["sales.opportunity.fetch"] }),
);
