import { eq } from 'drizzle-orm'
import dayjs from 'dayjs'
import { useDrizzle } from '@/lib/db.js'
import { chargePoint, connector, connectorStatusHistory } from '@/db/schema.js'
import type {
  StatusNotificationRequest,
  StatusNotificationResponse,
  OcppConnectionContext,
} from '../types.ts'

// Mirror the enum values defined in ocpp-schema.ts for type safety
type ConnectorStatus =
  | 'Available'
  | 'Preparing'
  | 'Charging'
  | 'SuspendedEVSE'
  | 'SuspendedEV'
  | 'Finishing'
  | 'Reserved'
  | 'Unavailable'
  | 'Faulted'

type ConnectorErrorCode =
  | 'NoError'
  | 'ConnectorLockFailure'
  | 'EVCommunicationError'
  | 'GroundFailure'
  | 'HighTemperature'
  | 'InternalError'
  | 'LocalListConflict'
  | 'OtherError'
  | 'OverCurrentFailure'
  | 'OverVoltage'
  | 'PowerMeterFailure'
  | 'PowerSwitchFailure'
  | 'ReaderFailure'
  | 'ResetFailure'
  | 'UnderVoltage'
  | 'WeakSignal'

export async function handleStatusNotification(
  payload: StatusNotificationRequest,
  ctx: OcppConnectionContext,
): Promise<StatusNotificationResponse> {
  const db = useDrizzle()

  // Retrieve the internal charge point id
  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}`)
  }

  const statusTimestamp = payload.timestamp ? dayjs(payload.timestamp).toDate() : dayjs().toDate()
  const connStatus = payload.status as ConnectorStatus
  const connErrorCode = payload.errorCode as ConnectorErrorCode

  // Upsert connector and return the internal id for history insertion
  const [upsertedConnector] = await db
    .insert(connector)
    .values({
      id: crypto.randomUUID(),
      chargePointId: cp.id,
      connectorId: payload.connectorId,
      status: connStatus,
      errorCode: connErrorCode,
      info: payload.info ?? null,
      vendorId: payload.vendorId ?? null,
      vendorErrorCode: payload.vendorErrorCode ?? null,
      lastStatusAt: statusTimestamp,
    })
    .onConflictDoUpdate({
      target: [connector.chargePointId, connector.connectorId],
      set: {
        status: connStatus,
        errorCode: connErrorCode,
        info: payload.info ?? null,
        vendorId: payload.vendorId ?? null,
        vendorErrorCode: payload.vendorErrorCode ?? null,
        lastStatusAt: statusTimestamp,
        updatedAt: dayjs().toDate(),
      },
    })
    .returning({ id: connector.id })

  if (upsertedConnector) {
    await db.insert(connectorStatusHistory).values({
      id: crypto.randomUUID(),
      connectorId: upsertedConnector.id,
      connectorNumber: payload.connectorId,
      status: connStatus,
      errorCode: connErrorCode,
      info: payload.info ?? null,
      vendorId: payload.vendorId ?? null,
      vendorErrorCode: payload.vendorErrorCode ?? null,
      statusTimestamp,
    })
  }

  return {}
}
