import { eq } from "drizzle-orm";
import dayjs from "dayjs";
import { useDrizzle } from "@/lib/db.js";
import { chargePoint, connector, transaction } from "@/db/schema.js";
import type {
  StartTransactionRequest,
  StartTransactionResponse,
  OcppConnectionContext,
} from "../types.ts";
import { resolveIdTagInfo } from "./authorize.ts";

export async function handleStartTransaction(
  payload: StartTransactionRequest,
  ctx: OcppConnectionContext,
): Promise<StartTransactionResponse> {
  const db = useDrizzle();

  // Resolve idTag authorization
  const idTagInfo = await resolveIdTagInfo(payload.idTag);

  // Find charge point
  const [cp] = await db
    .select({ id: chargePoint.id })
    .from(chargePoint)
    .where(eq(chargePoint.chargePointIdentifier, ctx.chargePointIdentifier))
    .limit(1);

  if (!cp) throw new Error(`ChargePoint not found: ${ctx.chargePointIdentifier}`);

  // Upsert connector — it may not exist yet if StatusNotification was skipped
  const [conn] = await db
    .insert(connector)
    .values({
      id: crypto.randomUUID(),
      chargePointId: cp.id,
      connectorId: payload.connectorId,
      status: "Charging",
      errorCode: "NoError",
      lastStatusAt: dayjs().toDate(),
    })
    .onConflictDoUpdate({
      target: [connector.chargePointId, connector.connectorId],
      set: { status: "Charging", updatedAt: dayjs().toDate() },
    })
    .returning({ id: connector.id });

  const rejected = idTagInfo.status !== "Accepted";
  const now = dayjs();

  // Insert transaction record regardless of auth status (OCPP spec requirement)
  const [tx] = await db
    .insert(transaction)
    .values({
      chargePointId: cp.id,
      connectorId: conn.id,
      connectorNumber: payload.connectorId,
      idTag: payload.idTag,
      idTagStatus: idTagInfo.status,
      startTimestamp: dayjs(payload.timestamp).toDate(),
      startMeterValue: payload.meterStart,
      reservationId: payload.reservationId ?? null,
      // If rejected, immediately close the transaction so it doesn't appear as in-progress
      ...(rejected && {
        stopTimestamp: now.toDate(),
        stopMeterValue: payload.meterStart,
        chargeAmount: 0,
        stopReason: "DeAuthorized",
      }),
    })
    .returning({ id: transaction.id });

  // If rejected, reset connector back to Available
  if (rejected) {
    await db
      .update(connector)
      .set({ status: "Available", updatedAt: now.toDate() })
      .where(eq(connector.id, conn.id));
  }

  console.log(
    `[OCPP] StartTransaction cp=${ctx.chargePointIdentifier} connector=${payload.connectorId} ` +
      `idTag=${payload.idTag} status=${idTagInfo.status} txId=${tx.id}`,
  );

  return { transactionId: tx.id, idTagInfo };
}
