import { z } from "zod";
import { ticketSchema, ticketStatusSchema, ticketWithDetailsSchema } from "@/lib/schemas/ticket";

type NormalizedTicketStatus = z.infer<typeof ticketStatusSchema>;

const STATUS_MAP: Record<string, NormalizedTicketStatus> = {
  NEW: "PENDING",
  PENDING: "PENDING",
  OPEN: "AWAITING_ATTENDANCE",
  AWAITING_ATTENDANCE: "AWAITING_ATTENDANCE",
  ON_HOLD: "PAUSED",
  PAUSED: "PAUSED",
  RESOLVED: "RESOLVED",
  CLOSED: "RESOLVED",
};

function normalizeTicketStatus(status: unknown): NormalizedTicketStatus {
  if (typeof status !== "string") return "PENDING";
  const normalized = STATUS_MAP[status.toUpperCase()];
  return normalized ?? "PENDING";
}

const DATE_ONLY_REGEX = /^\d{4}-\d{2}-\d{2}$/;

function normalizeCustomFieldDateValue(raw: unknown): string | null {
  if (raw === null || raw === undefined) {
    return null;
  }
  if (typeof raw === "string") {
    const trimmed = raw.trim();
    if (!trimmed) return null;
    if (DATE_ONLY_REGEX.test(trimmed)) {
      return trimmed;
    }
    const parsed = new Date(trimmed);
    if (Number.isNaN(parsed.getTime())) {
      return null;
    }
    return parsed.toISOString().slice(0, 10);
  }
  const date =
    raw instanceof Date
      ? raw
      : typeof raw === "number"
        ? new Date(raw)
        : null;
  if (!date || Number.isNaN(date.getTime())) {
    return null;
  }
  return date.toISOString().slice(0, 10);
}

// Server shapes: datas como number (epoch ms) e alguns nullables
// Relaxamos email/urls no shape do servidor para evitar que payloads parciais quebrem o app.
const serverUserSchema = z.object({
  id: z.string(),
  name: z.string(),
  email: z.string().optional(),
  avatarUrl: z.string().optional(),
  teams: z.array(z.string()).optional(),
});

const serverMachineSummarySchema = z.object({
  id: z.string().nullable().optional(),
  hostname: z.string().nullable().optional(),
  persona: z.string().nullable().optional(),
  assignedUserName: z.string().nullable().optional(),
  assignedUserEmail: z.string().nullable().optional(),
  status: z.string().nullable().optional(),
});

const serverTicketSchema = z.object({
  id: z.string(),
  reference: z.number(),
  tenantId: z.string(),
  subject: z.string(),
  summary: z.string().optional().nullable(),
  status: z.string(),
  priority: z.string(),
  channel: z.string(),
  queue: z.string().nullable(),
  formTemplate: z.string().nullable().optional(),
  formTemplateLabel: z.string().nullable().optional(),
  checklist: z
    .array(
      z.object({
        id: z.string(),
        text: z.string(),
        description: z.string().optional(),
        type: z.enum(["checkbox", "question"]).optional(),
        options: z.array(z.string()).optional(),
        answer: z.string().optional(),
        done: z.boolean(),
        required: z.boolean().optional(),
        templateId: z.string().optional(),
        templateItemId: z.string().optional(),
        templateDescription: z.string().optional(),
        createdAt: z.number().optional(),
        createdBy: z.string().optional(),
        doneAt: z.number().optional(),
        doneBy: z.string().optional(),
      }),
    )
    .optional(),
  requester: serverUserSchema,
  assignee: serverUserSchema.nullable(),
  company: z
    .object({ id: z.string(), name: z.string(), isAvulso: z.boolean().optional() })
    .optional()
    .nullable(),
  machine: serverMachineSummarySchema.optional().nullable(),
  slaPolicy: z.any().nullable().optional(),
  slaSnapshot: z
    .object({
      categoryId: z.any().optional(),
      categoryName: z.string().optional(),
      priority: z.string().optional(),
      responseTargetMinutes: z.number().optional().nullable(),
      responseMode: z.string().optional(),
      solutionTargetMinutes: z.number().optional().nullable(),
      solutionMode: z.string().optional(),
      alertThreshold: z.number().optional(),
      pauseStatuses: z.array(z.string()).optional(),
    })
    .nullable()
    .optional(),
  slaResponseDueAt: z.number().nullable().optional(),
  slaSolutionDueAt: z.number().nullable().optional(),
  slaResponseStatus: z.string().nullable().optional(),
  slaSolutionStatus: z.string().nullable().optional(),
  slaPausedAt: z.number().nullable().optional(),
  slaPausedBy: z.string().nullable().optional(),
  slaPausedMs: z.number().nullable().optional(),
  dueAt: z.number().nullable().optional(),
  visitStatus: z.string().nullable().optional(),
  visitPerformedAt: z.number().nullable().optional(),
  firstResponseAt: z.number().nullable().optional(),
  resolvedAt: z.number().nullable().optional(),
  closedAt: z.number().nullable().optional(),
  reopenDeadline: z.number().nullable().optional(),
  reopenWindowDays: z.number().nullable().optional(),
  reopenedAt: z.number().nullable().optional(),
  updatedAt: z.number(),
  createdAt: z.number(),
  tags: z.array(z.string()).default([]).optional(),
  lastTimelineEntry: z.string().nullable().optional(),
  metrics: z.any().nullable().optional(),
  csatScore: z.number().nullable().optional(),
  csatMaxScore: z.number().nullable().optional(),
  csatComment: z.string().nullable().optional(),
  csatRatedAt: z.number().nullable().optional(),
  csatRatedBy: z.string().nullable().optional(),
  category: z
    .object({
      id: z.string(),
      name: z.string(),
    })
    .nullable()
    .optional(),
  subcategory: z
    .object({
      id: z.string(),
      name: z.string(),
      categoryId: z.string().optional(),
    })
    .nullable()
    .optional(),
  workSummary: z
    .object({
      totalWorkedMs: z.number(),
      internalWorkedMs: z.number().optional(),
      externalWorkedMs: z.number().optional(),
      serverNow: z.number().optional(),
      activeSession: z
        .object({
          id: z.string(),
          agentId: z.string(),
          startedAt: z.number(),
        })
        .nullable(),
      perAgentTotals: z
        .array(
          z.object({
            agentId: z.string(),
            agentName: z.string().nullable().optional(),
            agentEmail: z.string().nullable().optional(),
            avatarUrl: z.string().nullable().optional(),
            totalWorkedMs: z.number(),
            internalWorkedMs: z.number().optional(),
            externalWorkedMs: z.number().optional(),
          }),
        )
        .optional(),
    })
    .nullable()
    .optional(),
});

const serverAttachmentSchema = z.object({
  id: z.any(),
  name: z.string(),
  size: z.number().optional(),
  url: z.string().url().optional(),
});

const serverCommentSchema = z.object({
  id: z.string(),
  author: serverUserSchema,
  visibility: z.string(),
  body: z.string(),
  attachments: z.array(serverAttachmentSchema).default([]).optional(),
  createdAt: z.number(),
  updatedAt: z.number(),
});

const serverEventSchema = z.object({
  id: z.string(),
  type: z.string(),
  payload: z.any().optional(),
  createdAt: z.number(),
});

const serverCustomFieldValueSchema = z.object({
  label: z.string(),
  type: z.string(),
  value: z.any().optional(),
  displayValue: z.string().optional(),
});

const serverTicketWithDetailsSchema = serverTicketSchema.extend({
  description: z.string().optional().nullable(),
  customFields: z.record(z.string(), serverCustomFieldValueSchema).optional(),
  timeline: z.array(serverEventSchema),
  comments: z.array(serverCommentSchema),
  company: z
    .object({ id: z.string(), name: z.string(), isAvulso: z.boolean().optional() })
    .optional()
    .nullable(),
});

export function mapTicketFromServer(input: unknown) {
  const {
    csatScore,
    csatMaxScore,
    csatComment,
    csatRatedAt,
    csatRatedBy,
    ...base
  } = serverTicketSchema.parse(input);
  const s = { csatScore, csatMaxScore, csatComment, csatRatedAt, csatRatedBy, ...base };
  const checklist = (s.checklist ?? []).map((item) => ({
    ...item,
    required: item.required ?? true,
  }));
  const slaSnapshot = s.slaSnapshot
    ? {
        categoryId: s.slaSnapshot.categoryId ? String(s.slaSnapshot.categoryId) : undefined,
        categoryName: s.slaSnapshot.categoryName ?? undefined,
        priority: s.slaSnapshot.priority ?? "",
        responseTargetMinutes: s.slaSnapshot.responseTargetMinutes ?? null,
        responseMode: (s.slaSnapshot.responseMode ?? "calendar") as "business" | "calendar",
        solutionTargetMinutes: s.slaSnapshot.solutionTargetMinutes ?? null,
        solutionMode: (s.slaSnapshot.solutionMode ?? "calendar") as "business" | "calendar",
        alertThreshold: typeof s.slaSnapshot.alertThreshold === "number" ? s.slaSnapshot.alertThreshold : null,
        pauseStatuses: s.slaSnapshot.pauseStatuses ?? [],
      }
    : null;
  const ui = {
    ...base,
    status: normalizeTicketStatus(s.status),
    checklist,
    company: s.company
      ? { id: s.company.id, name: s.company.name, isAvulso: s.company.isAvulso ?? false }
      : undefined,
    machine: s.machine
      ? {
          id: s.machine.id ?? null,
          hostname: s.machine.hostname ?? null,
          persona: s.machine.persona ?? null,
          assignedUserName: s.machine.assignedUserName ?? null,
          assignedUserEmail: s.machine.assignedUserEmail ?? null,
          status: s.machine.status ?? null,
        }
      : null,
    category: s.category ?? undefined,
    subcategory: s.subcategory ?? undefined,
    lastTimelineEntry: s.lastTimelineEntry ?? undefined,
    updatedAt: new Date(s.updatedAt),
    createdAt: new Date(s.createdAt),
    dueAt: s.dueAt ? new Date(s.dueAt) : null,
    firstResponseAt: s.firstResponseAt ? new Date(s.firstResponseAt) : null,
    resolvedAt: s.resolvedAt ? new Date(s.resolvedAt) : null,
    closedAt: s.closedAt ? new Date(s.closedAt) : null,
    reopenDeadline: typeof s.reopenDeadline === "number" ? s.reopenDeadline : null,
    reopenWindowDays: typeof s.reopenWindowDays === "number" ? s.reopenWindowDays : null,
    reopenedAt: typeof s.reopenedAt === "number" ? s.reopenedAt : null,
    csatScore: typeof csatScore === "number" ? csatScore : null,
    csatMaxScore: typeof csatMaxScore === "number" ? csatMaxScore : null,
    csatComment: typeof csatComment === "string" && csatComment.trim().length > 0 ? csatComment.trim() : null,
    csatRatedAt: csatRatedAt ? new Date(csatRatedAt) : null,
    csatRatedBy: csatRatedBy ?? null,
    formTemplateLabel: base.formTemplateLabel ?? null,
    slaSnapshot,
    slaResponseDueAt: s.slaResponseDueAt ? new Date(s.slaResponseDueAt) : null,
    slaSolutionDueAt: s.slaSolutionDueAt ? new Date(s.slaSolutionDueAt) : null,
    slaResponseStatus: typeof s.slaResponseStatus === "string" ? (s.slaResponseStatus as string) : null,
    slaSolutionStatus: typeof s.slaSolutionStatus === "string" ? (s.slaSolutionStatus as string) : null,
    slaPausedAt: s.slaPausedAt ? new Date(s.slaPausedAt) : null,
    slaPausedBy: s.slaPausedBy ?? null,
    slaPausedMs: typeof s.slaPausedMs === "number" ? s.slaPausedMs : null,
    visitStatus: typeof s.visitStatus === "string" ? s.visitStatus : null,
    visitPerformedAt: s.visitPerformedAt ? new Date(s.visitPerformedAt) : null,
    workSummary: s.workSummary
      ? {
          totalWorkedMs: s.workSummary.totalWorkedMs,
          internalWorkedMs: s.workSummary.internalWorkedMs ?? 0,
          externalWorkedMs: s.workSummary.externalWorkedMs ?? 0,
          serverNow: s.workSummary.serverNow,
          activeSession: s.workSummary.activeSession
            ? {
                ...s.workSummary.activeSession,
                startedAt: new Date(s.workSummary.activeSession.startedAt),
              }
            : null,
          perAgentTotals: (s.workSummary.perAgentTotals ?? []).map((item) => ({
            agentId: item.agentId,
            agentName: item.agentName ?? null,
            agentEmail: item.agentEmail ?? null,
            avatarUrl: item.avatarUrl ?? null,
            totalWorkedMs: item.totalWorkedMs,
            internalWorkedMs: item.internalWorkedMs ?? 0,
            externalWorkedMs: item.externalWorkedMs ?? 0,
          })),
        }
      : undefined,
  };
  return ui as unknown as z.infer<typeof ticketSchema>;
}

export function mapTicketsFromServerList(arr: unknown[]) {
  return arr.map(mapTicketFromServer);
}

export function mapTicketWithDetailsFromServer(input: unknown) {
  const {
    csatScore,
    csatMaxScore,
    csatComment,
    csatRatedAt,
    csatRatedBy,
    ...base
  } = serverTicketWithDetailsSchema.parse(input);
  const s = { csatScore, csatMaxScore, csatComment, csatRatedAt, csatRatedBy, ...base };
  const checklist = (s.checklist ?? []).map((item) => ({
    ...item,
    required: item.required ?? true,
  }));
  const slaSnapshot = s.slaSnapshot
    ? {
        categoryId: s.slaSnapshot.categoryId ? String(s.slaSnapshot.categoryId) : undefined,
        categoryName: s.slaSnapshot.categoryName ?? undefined,
        priority: s.slaSnapshot.priority ?? "",
        responseTargetMinutes: s.slaSnapshot.responseTargetMinutes ?? null,
        responseMode: (s.slaSnapshot.responseMode ?? "calendar") as "business" | "calendar",
        solutionTargetMinutes: s.slaSnapshot.solutionTargetMinutes ?? null,
        solutionMode: (s.slaSnapshot.solutionMode ?? "calendar") as "business" | "calendar",
        alertThreshold: typeof s.slaSnapshot.alertThreshold === "number" ? s.slaSnapshot.alertThreshold : null,
        pauseStatuses: s.slaSnapshot.pauseStatuses ?? [],
      }
    : null;
  const customFields = Object.entries(s.customFields ?? {}).reduce<
    Record<string, { label: string; type: string; value?: unknown; displayValue?: string }>
  >(
    (acc, [key, value]) => {
      let parsedValue: unknown = value.value;
      if (value.type === "date") {
        parsedValue = normalizeCustomFieldDateValue(value.value) ?? value.value;
      }
      acc[key] = {
        label: value.label,
        type: value.type,
        value: parsedValue,
        displayValue: value.displayValue,
      };
      return acc;
    },
    {}
  );
  const ui = {
    ...base,
    customFields,
    status: normalizeTicketStatus(base.status),
    checklist,
    category: base.category ?? undefined,
    subcategory: base.subcategory ?? undefined,
    lastTimelineEntry: base.lastTimelineEntry ?? undefined,
    updatedAt: new Date(base.updatedAt),
    createdAt: new Date(base.createdAt),
    dueAt: base.dueAt ? new Date(base.dueAt) : null,
    visitStatus: typeof base.visitStatus === "string" ? base.visitStatus : null,
    visitPerformedAt: base.visitPerformedAt ? new Date(base.visitPerformedAt) : null,
    firstResponseAt: base.firstResponseAt ? new Date(base.firstResponseAt) : null,
    resolvedAt: base.resolvedAt ? new Date(base.resolvedAt) : null,
    closedAt: base.closedAt ? new Date(base.closedAt) : null,
    reopenDeadline: typeof base.reopenDeadline === "number" ? base.reopenDeadline : null,
    reopenWindowDays: typeof base.reopenWindowDays === "number" ? base.reopenWindowDays : null,
    reopenedAt: typeof base.reopenedAt === "number" ? base.reopenedAt : null,
    csatScore: typeof csatScore === "number" ? csatScore : null,
    csatMaxScore: typeof csatMaxScore === "number" ? csatMaxScore : null,
    csatComment: typeof csatComment === "string" && csatComment.trim().length > 0 ? csatComment.trim() : null,
    csatRatedAt: csatRatedAt ? new Date(csatRatedAt) : null,
    csatRatedBy: csatRatedBy ?? null,
    company: base.company ? { id: base.company.id, name: base.company.name, isAvulso: base.company.isAvulso ?? false } : undefined,
    machine: base.machine
      ? {
          id: base.machine.id ?? null,
          hostname: base.machine.hostname ?? null,
          persona: base.machine.persona ?? null,
          assignedUserName: base.machine.assignedUserName ?? null,
          assignedUserEmail: base.machine.assignedUserEmail ?? null,
          status: base.machine.status ?? null,
        }
      : null,
    slaSnapshot,
    slaResponseDueAt: base.slaResponseDueAt ? new Date(base.slaResponseDueAt) : null,
    slaSolutionDueAt: base.slaSolutionDueAt ? new Date(base.slaSolutionDueAt) : null,
    slaResponseStatus: typeof base.slaResponseStatus === "string" ? (base.slaResponseStatus as string) : null,
    slaSolutionStatus: typeof base.slaSolutionStatus === "string" ? (base.slaSolutionStatus as string) : null,
    slaPausedAt: base.slaPausedAt ? new Date(base.slaPausedAt) : null,
    slaPausedBy: base.slaPausedBy ?? null,
    slaPausedMs: typeof base.slaPausedMs === "number" ? base.slaPausedMs : null,
    timeline: base.timeline.map((e) => ({ ...e, createdAt: new Date(e.createdAt) })),
    comments: base.comments.map((c) => ({
      ...c,
      createdAt: new Date(c.createdAt),
      updatedAt: new Date(c.updatedAt),
    })),
    workSummary: base.workSummary
      ? {
          totalWorkedMs: base.workSummary.totalWorkedMs,
          internalWorkedMs: base.workSummary.internalWorkedMs ?? 0,
          externalWorkedMs: base.workSummary.externalWorkedMs ?? 0,
          serverNow: base.workSummary.serverNow,
          activeSession: base.workSummary.activeSession
            ? {
                ...base.workSummary.activeSession,
                startedAt: new Date(base.workSummary.activeSession.startedAt),
              }
            : null,
          perAgentTotals: (base.workSummary.perAgentTotals ?? []).map((item) => ({
            agentId: item.agentId,
            agentName: item.agentName ?? null,
            agentEmail: item.agentEmail ?? null,
            avatarUrl: item.avatarUrl ?? null,
            totalWorkedMs: item.totalWorkedMs,
            internalWorkedMs: item.internalWorkedMs ?? 0,
            externalWorkedMs: item.externalWorkedMs ?? 0,
          })),
        }
      : undefined,
  };
  return ui as unknown as z.infer<typeof ticketWithDetailsSchema>;
}
