import type { SupabaseClient, User } from "@supabase/supabase-js";

import type { CompanyContext } from "@/lib/company-context";
import { mensagemDaTrava, notasQueTravamOPedido } from "@/lib/fiscal/servidor/travas";
import { createAdminClient } from "@/lib/supabase-server";

/**
 * Mudança de status, etapa e cronômetro de um pedido.
 *
 * Além de gravar o estado no pedido, cada mudança deixa rastro em
 * `order_production_events`: a etapa anterior fecha e a nova abre; o
 * cronômetro abre uma sessão ao iniciar e fecha ao pausar ou sair de
 * produção. É desse rastro que saem o tempo por etapa, o tempo útil e o
 * lead time — o acumulado do pedido continua existindo, mas é um resumo.
 *
 * O primeiro início e a conclusão vão para colunas próprias, que ninguém
 * zera: `production_started_at` é o início da sessão atual e volta a nulo
 * ao pausar, e por isso não serve para a linha do tempo.
 */

type Source = "manual" | "kanban";

type ClientInfo = {
  name?: string | null;
  company_name?: string | null;
  whatsapp?: string | null;
  whatsapp_notify?: boolean | null;
};

type OrderBeforeRow = {
  id: string;
  status: string;
  number?: number | null;
  client_id?: string | null;
  user_id: string;
  production_started_at?: string | null;
  production_started_by?: string | null;
  production_accumulated_seconds?: number | null;
  production_last_stopped_at?: string | null;
  production_stage_id?: string | null;
  production_first_started_at?: string | null;
  production_finished_at?: string | null;
  clients?: ClientInfo | ClientInfo[] | null;
};

const statusLabel: Record<string, string> = {
  pending_production: "Aguardando",
  in_production: "Em Producao",
  finished: "Finalizado",
  delivered: "Entregue",
  canceled: "Cancelado",
};

function toSecondsDiff(fromIso: string, nowMs: number) {
  const fromMs = new Date(fromIso).getTime();
  if (!Number.isFinite(fromMs)) return 0;
  return Math.max(0, Math.floor((nowMs - fromMs) / 1000));
}

export type CorpoDaMudancaDeStatus = {
  id?: unknown;
  status?: unknown;
  source?: unknown;
  start_timer?: unknown;
  pause_timer?: unknown;
  production_stage_id?: unknown;
  production_stage_label?: unknown;
} | null;

export type RespostaDaMudancaDeStatus = { status: number; corpo: Record<string, unknown> };

function resposta(corpo: Record<string, unknown>, init?: { status: number }): RespostaDaMudancaDeStatus {
  return { status: init?.status ?? 200, corpo };
}

/**
 * O trabalho da rota /api/orders/status, fora dela para o assistente usar o
 * mesmo caminho (mesmas travas, mesmo rastro). Quem chama já conferiu a
 * pessoa e a permissão "orders.write".
 */
export async function mudarStatusDoPedido(
  supabase: SupabaseClient,
  user: User,
  companyCtx: CompanyContext,
  body: CorpoDaMudancaDeStatus
): Promise<RespostaDaMudancaDeStatus> {
  const id = typeof body?.id === "string" ? body.id : "";
  const newStatus = typeof body?.status === "string" ? body.status : "";
  const source = (typeof body?.source === "string" ? body.source : "manual") as Source;
  const startTimer = body?.start_timer === true;
  const pauseTimer = body?.pause_timer === true;
  const hasStageId = Object.prototype.hasOwnProperty.call(body ?? {}, "production_stage_id");
  const newStageId =
    hasStageId && typeof body?.production_stage_id === "string"
      ? body.production_stage_id
      : hasStageId
        ? null
        : null;
  const stageLabel = typeof body?.production_stage_label === "string" ? body.production_stage_label : "";

  if (!id) return resposta({ error: "Campo 'id' e obrigatorio." }, { status: 400 });
  if (!newStatus) return resposta({ error: "Campo 'status' e obrigatorio." }, { status: 400 });

  const tenantIds = companyCtx.tenantIds?.length ? companyCtx.tenantIds : [user.id];

  const { data: rawOrderBefore, error: beforeErr } = await supabase
    .from("orders")
    .select(
      "id,status,number,client_id,user_id,production_started_at,production_started_by,production_accumulated_seconds,production_last_stopped_at,production_stage_id,production_first_started_at,production_finished_at,clients(name,company_name,whatsapp,whatsapp_notify)"
    )
    .eq("id", id)
    .in("user_id", tenantIds)
    .maybeSingle();

  if (beforeErr) return resposta({ error: beforeErr.message }, { status: 400 });
  if (!rawOrderBefore) return resposta({ error: "Pedido nao encontrado." }, { status: 404 });

  const orderBefore = rawOrderBefore as unknown as OrderBeforeRow;
  const prevStatus = orderBefore.status;

  if (newStatus === "canceled") {
    /* Pedido com nota fiscal valendo só cancela depois da nota. */
    const notasQueTravam = await notasQueTravamOPedido(id);
    if (notasQueTravam.length) {
      return resposta({ error: mensagemDaTrava(notasQueTravam, "cancelar") }, { status: 409 });
    }

    const { count: paymentCount, error: paymentsError } = await supabase
      .from("payments")
      .select("id", { count: "exact", head: true })
      .eq("order_id", id)
      .in("user_id", tenantIds);

    if (paymentsError) {
      return resposta({ error: "Falha ao validar pagamentos do pedido." }, { status: 500 });
    }

    if ((paymentCount ?? 0) > 0) {
      return resposta(
        {
          error:
            "Este pedido possui pagamentos registrados. Remova ou estorne os recebimentos antes de cancelar o pedido.",
        },
        { status: 409 }
      );
    }
  }

  const now = new Date();
  const nowIso = now.toISOString();
  const nowMs = now.getTime();

  const wasRunning = Boolean(orderBefore.production_started_at);
  const accumulatedBefore = Number(orderBefore.production_accumulated_seconds || 0);
  const statusChanged = prevStatus !== newStatus;
  const previousStageId = orderBefore.production_stage_id ?? null;
  const stageChanged = hasStageId && previousStageId !== newStageId;

  const shouldStartTimer =
    newStatus === "in_production" &&
    !wasRunning &&
    (statusChanged || startTimer);
  const shouldPauseTimer =
    newStatus === "in_production" &&
    wasRunning &&
    pauseTimer;
  const shouldStopTimer = newStatus !== "in_production" && wasRunning;

  if (!statusChanged && !shouldStartTimer && !shouldPauseTimer && !shouldStopTimer && !stageChanged) {
    return resposta({ ok: true, unchanged: true });
  }

  let nextAccumulated = accumulatedBefore;
  if ((shouldStopTimer || shouldPauseTimer) && orderBefore.production_started_at) {
    nextAccumulated += toSecondsDiff(orderBefore.production_started_at, nowMs);
  }

  const updatePayload: Record<string, unknown> = {
    status: newStatus,
    updated_at: nowIso,
    production_updated_at: nowIso,
  };

  const nextStageId = stageChanged
    ? newStageId
    : statusChanged && !hasStageId
      ? null
      : previousStageId;

  if (stageChanged || (statusChanged && hasStageId)) {
    updatePayload.production_stage_id = newStageId;
  } else if (statusChanged && !hasStageId) {
    updatePayload.production_stage_id = null;
  }

  if (shouldStartTimer) {
    updatePayload.production_started_at = nowIso;
    updatePayload.production_started_by = user.id;
    /* O primeiro início fica guardado para sempre: a linha do tempo lê
       daqui, não do início da sessão atual. */
    if (!orderBefore.production_first_started_at) {
      updatePayload.production_first_started_at = nowIso;
    }
  }

  if (shouldStopTimer || shouldPauseTimer) {
    updatePayload.production_started_at = null;
    updatePayload.production_last_stopped_at = nowIso;
    updatePayload.production_accumulated_seconds = nextAccumulated;
  }

  const isFinal = newStatus === "finished" || newStatus === "delivered";
  if (statusChanged && isFinal && !orderBefore.production_finished_at) {
    updatePayload.production_finished_at = nowIso;
  }

  const { error: updateError } = await supabase
    .from("orders")
    .update(updatePayload)
    .eq("id", id)
    .in("user_id", tenantIds);

  if (updateError) return resposta({ error: updateError.message }, { status: 400 });

  /* O rastro. Uma falha aqui não desfaz a mudança de status — ela já
     aconteceu —, mas é informada na resposta em vez de engolida. */
  const eventErrors: string[] = [];
  const admin = createAdminClient();

  if (statusChanged || stageChanged) {
    const { error: closeError } = await admin
      .from("order_production_events")
      .update({ ended_at: nowIso, ended_by: user.id })
      .eq("order_id", id)
      .eq("kind", "etapa")
      .is("ended_at", null);
    if (closeError) eventErrors.push(closeError.message);

    const { error: openError } = await admin.from("order_production_events").insert({
      user_id: orderBefore.user_id,
      order_id: id,
      kind: "etapa",
      status: newStatus,
      stage_id: nextStageId,
      stage_label: stageLabel.trim() || null,
      started_at: nowIso,
      started_by: user.id,
    });
    if (openError) eventErrors.push(openError.message);
  }

  if (shouldStartTimer) {
    const { error: timerError } = await admin.from("order_production_events").insert({
      user_id: orderBefore.user_id,
      order_id: id,
      kind: "cronometro",
      status: newStatus,
      stage_id: nextStageId,
      stage_label: stageLabel.trim() || null,
      started_at: nowIso,
      started_by: user.id,
    });
    if (timerError) eventErrors.push(timerError.message);
  }

  if (shouldStopTimer || shouldPauseTimer) {
    const { error: timerError } = await admin
      .from("order_production_events")
      .update({ ended_at: nowIso, ended_by: user.id })
      .eq("order_id", id)
      .eq("kind", "cronometro")
      .is("ended_at", null);
    if (timerError) eventErrors.push(timerError.message);
  }

  if (eventErrors.length > 0) {
    console.error("Falha ao registrar eventos de producao:", eventErrors);
  }

  if (statusChanged) {
    const stageSuffix = stageChanged && stageLabel ? ` (Etapa: ${stageLabel})` : "";
    const note =
      source === "kanban"
        ? `Status alterado via Kanban para ${statusLabel[newStatus] || newStatus}${stageSuffix}`
        : `Status alterado manualmente para ${statusLabel[newStatus] || newStatus}${stageSuffix}`;

    const { error: historyError } = await supabase.from("order_history").insert({
      order_id: id,
      user_id: user.id,
      status_from: prevStatus || null,
      status_to: newStatus,
      notes: note,
    });

    if (historyError) {
      return resposta({ error: historyError.message }, { status: 400 });
    }
  } else if (stageChanged) {
    const note =
      source === "kanban"
        ? `Etapa do Kanban alterada para ${stageLabel || "nova etapa"}.`
        : `Etapa alterada manualmente para ${stageLabel || "nova etapa"}.`;
    const { error: historyError } = await supabase.from("order_history").insert({
      order_id: id,
      user_id: user.id,
      status_from: prevStatus || null,
      status_to: newStatus,
      notes: note,
    });

    if (historyError) {
      return resposta({ error: historyError.message }, { status: 400 });
    }
  } else if (shouldStartTimer || shouldPauseTimer || shouldStopTimer) {
    const timerNote = shouldStartTimer
      ? "Cronometro de producao iniciado."
      : "Cronometro de producao pausado.";
    const { error: historyError } = await supabase.from("order_history").insert({
      order_id: id,
      user_id: user.id,
      status_from: prevStatus || null,
      status_to: newStatus,
      notes: timerNote,
    });

    if (historyError) {
      return resposta({ error: historyError.message }, { status: 400 });
    }
  }

  return resposta({
    ok: true,
    events_error: eventErrors.length > 0 ? eventErrors.join("; ") : null,
    order: {
      id,
      status: newStatus,
      production_stage_id: nextStageId,
      production_started_at: (shouldStopTimer || shouldPauseTimer)
        ? null
        : shouldStartTimer
          ? nowIso
          : orderBefore.production_started_at || null,
      production_started_by: shouldStartTimer
        ? user.id
        : orderBefore.production_started_by || null,
      production_accumulated_seconds: (shouldStopTimer || shouldPauseTimer)
        ? nextAccumulated
        : accumulatedBefore,
      production_last_stopped_at: (shouldStopTimer || shouldPauseTimer)
        ? nowIso
        : orderBefore.production_last_stopped_at || null,
      production_first_started_at:
        (updatePayload.production_first_started_at as string | undefined) ??
        orderBefore.production_first_started_at ??
        null,
      production_finished_at:
        (updatePayload.production_finished_at as string | undefined) ?? orderBefore.production_finished_at ?? null,
      production_updated_at: nowIso,
    },
  });
}
