import { db } from "@/app/db/db";
import { pibiRequests } from "@/app/db/schema/pibi_requests";
import { eq, and } from "drizzle-orm";
import { syncMeasuresFieldToHubSpot as defaultSyncMeasuresField } from "@/app/lib/hubspot/dealSync";

export { PIBI_ORDERED_TEXT_PROP } from "./createPibiRequests";
import { PIBI_ORDERED_TEXT_PROP } from "./createPibiRequests";

// ---------------------------------------------------------------------------
// Injectable dep types
// ---------------------------------------------------------------------------

export type RunUpdatePibiTx = (params: {
  id: bigint;
  dealId: string;
  updates: PibiRequestUpdates;
}) => Promise<{ allMeasureNames: string[] }>;

export type SyncMeasuresField = typeof defaultSyncMeasuresField;

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

export interface PibiRequestUpdates {
  measureName?: string;
  orderedAt?: Date;
  completedAt?: Date | null;
}

export type UpdatePibiRequestResult =
  | { ok: true; hubspotSync: "ok" | "failed"; hubspotError?: string }
  | { ok: false; error: string };

export interface UpdatePibiRequestInput {
  id: bigint;
  dealId: string;
  updates: PibiRequestUpdates;
  deps?: {
    runUpdateTx?: RunUpdatePibiTx;
    syncMeasuresField?: SyncMeasuresField;
  };
}

// ---------------------------------------------------------------------------
// Default DB-backed implementation
// ---------------------------------------------------------------------------

const defaultRunUpdateTx: RunUpdatePibiTx = async ({ id, dealId, updates }) => {
  return await db.transaction(async (tx) => {
    const result = await tx
      .update(pibiRequests)
      .set({
        ...(updates.measureName !== undefined && { measureName: updates.measureName }),
        ...(updates.orderedAt !== undefined && { orderedAt: updates.orderedAt }),
        ...(updates.completedAt !== undefined && { completedAt: updates.completedAt }),
      })
      .where(and(eq(pibiRequests.id, id), eq(pibiRequests.hubspotDealId, dealId)))
      .returning({ id: pibiRequests.id });

    if (result.length === 0) {
      throw new Error("PIBI request not found");
    }

    const allRows = await tx
      .select({ measureName: pibiRequests.measureName })
      .from(pibiRequests)
      .where(eq(pibiRequests.hubspotDealId, dealId));

    return { allMeasureNames: allRows.map((r) => r.measureName) };
  });
};

// ---------------------------------------------------------------------------
// Service entry-point
// ---------------------------------------------------------------------------

export async function updatePibiRequest(
  input: UpdatePibiRequestInput,
): Promise<UpdatePibiRequestResult> {
  const runUpdateTx = input.deps?.runUpdateTx ?? defaultRunUpdateTx;
  const syncMeasuresField = input.deps?.syncMeasuresField ?? defaultSyncMeasuresField;

  let txResult: { allMeasureNames: string[] };
  try {
    txResult = await runUpdateTx({
      id: input.id,
      dealId: input.dealId,
      updates: input.updates,
    });
  } catch (err) {
    const message = err instanceof Error ? err.message : "Failed to update PIBI request";
    return { ok: false, error: message };
  }

  const syncResult = await syncMeasuresField({
    hubspotDealId: input.dealId,
    propName: PIBI_ORDERED_TEXT_PROP,
    measureNames: txResult.allMeasureNames,
  });

  if (syncResult.ok) {
    return { ok: true, hubspotSync: "ok" };
  }

  return {
    ok: true,
    hubspotSync: "failed",
    hubspotError: syncResult.error,
  };
}
