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

export const PIBI_ORDERED_TEXT_PROP = "measures_for_pibi_ordered_text";

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

export type RunCreatePibiTx = (params: {
  dealId: string;
  portfolioId: bigint;
  measureNames: string[];
  orderedAt: Date;
  userId: bigint;
}) => Promise<{ insertedRowIds: bigint[]; allMeasureNames: string[] }>;

export type SyncMeasuresField = typeof defaultSyncMeasuresField;

export type StampPushedAt = (rowIds: bigint[]) => Promise<void>;

// ---------------------------------------------------------------------------
// Result type
// ---------------------------------------------------------------------------

export type CreatePibiRequestsResult =
  | {
      ok: true;
      insertedRowIds: bigint[];
      hubspotSync: "ok" | "failed";
      hubspotError?: string;
    }
  | { ok: false; error: string };

// ---------------------------------------------------------------------------
// Input
// ---------------------------------------------------------------------------

export interface CreatePibiRequestsInput {
  dealId: string;
  portfolioId: bigint;
  measureNames: string[];
  /** Defaults to now() when omitted. */
  orderedAt?: Date;
  userId: bigint;
  deps?: {
    runCreateTx?: RunCreatePibiTx;
    syncMeasuresField?: SyncMeasuresField;
    stampPushedAt?: StampPushedAt;
  };
}

// ---------------------------------------------------------------------------
// Default DB-backed implementations
// ---------------------------------------------------------------------------

const defaultRunCreateTx: RunCreatePibiTx = async ({
  dealId,
  portfolioId,
  measureNames,
  orderedAt,
  userId,
}) => {
  return await db.transaction(async (tx) => {
    const inserted = await tx
      .insert(pibiRequests)
      .values(
        measureNames.map((measureName) => ({
          hubspotDealId: dealId,
          portfolioId,
          measureName,
          orderedAt,
          createdByUserId: userId,
        })),
      )
      .returning({ id: pibiRequests.id });

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

    return {
      insertedRowIds: inserted.map((r) => r.id),
      allMeasureNames: allRows.map((r) => r.measureName),
    };
  });
};

const defaultStampPushedAt: StampPushedAt = async (rowIds) => {
  if (rowIds.length === 0) return;
  for (const rowId of rowIds) {
    await db
      .update(pibiRequests)
      .set({ pushedAt: new Date() })
      .where(eq(pibiRequests.id, rowId));
  }
};

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

export async function createPibiRequests(
  input: CreatePibiRequestsInput,
): Promise<CreatePibiRequestsResult> {
  if (input.measureNames.length === 0) {
    return { ok: false, error: "measureNames must not be empty" };
  }

  const runCreateTx = input.deps?.runCreateTx ?? defaultRunCreateTx;
  const syncMeasuresField = input.deps?.syncMeasuresField ?? defaultSyncMeasuresField;
  const stampPushedAt = input.deps?.stampPushedAt ?? defaultStampPushedAt;
  const orderedAt = input.orderedAt ?? new Date();

  let txResult: { insertedRowIds: bigint[]; allMeasureNames: string[] };
  try {
    txResult = await runCreateTx({
      dealId: input.dealId,
      portfolioId: input.portfolioId,
      measureNames: input.measureNames,
      orderedAt,
      userId: input.userId,
    });
  } catch (err) {
    const message = err instanceof Error ? err.message : "Failed to create PIBI requests";
    return { ok: false, error: message };
  }

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

  if (syncResult.ok) {
    try {
      await stampPushedAt(txResult.insertedRowIds);
    } catch (err) {
      console.error("[createPibiRequests] failed to stamp pushed_at", {
        rowIds: txResult.insertedRowIds.map(String),
        error: err,
      });
    }
    return {
      ok: true,
      insertedRowIds: txResult.insertedRowIds,
      hubspotSync: "ok",
    };
  }

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