import { db } from "@/app/db/db";
import { and, eq, inArray } from "drizzle-orm";
import {
  dealMeasureApprovals,
  dealMeasureApprovalEvents,
} from "@/app/db/schema/approvals";
import { user } from "@/app/db/schema/users";
import {
  syncMeasureApprovalsToHubSpot,
  syncMeasuresFieldToHubSpot,
} from "@/app/lib/hubspot/dealSync";
import { APPROVED_MEASURES_PROP } from "@/app/lib/instructMeasure";

export interface BulkApproveChange {
  hubspotDealId: string;
  measureName: string;
  approved: boolean;
}

export interface BulkApproveTxResult {
  approvedMeasuresByDeal: Record<string, string[]>;
}

export type RunBulkApproveTx = (params: {
  changes: BulkApproveChange[];
  userId: bigint;
}) => Promise<BulkApproveTxResult>;

export type SyncApprovalsForDeals = (params: {
  approvedMeasuresByDeal: Record<string, string[]>;
  actedByEmail: string;
  actedAt: Date;
}) => Promise<{ ok: true } | { ok: false; error: string }>;

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

export interface BulkApproveInput {
  changes: BulkApproveChange[];
  userId: bigint;
  actedByEmail?: string;
  deps?: {
    runBulkApproveTx?: RunBulkApproveTx;
    syncApprovals?: SyncApprovalsForDeals;
  };
}

const defaultRunBulkApproveTx: RunBulkApproveTx = async ({ changes, userId }) => {
  return db.transaction(async (tx) => {
    const now = new Date();

    for (const change of changes) {
      await tx
        .insert(dealMeasureApprovals)
        .values({
          hubspotDealId: change.hubspotDealId,
          measureName: change.measureName,
          isApproved: change.approved,
          approvedBy: userId,
          approvedAt: now,
        })
        .onConflictDoUpdate({
          target: [dealMeasureApprovals.hubspotDealId, dealMeasureApprovals.measureName],
          set: { isApproved: change.approved, approvedBy: userId, approvedAt: now },
        });

      await tx.insert(dealMeasureApprovalEvents).values({
        hubspotDealId: change.hubspotDealId,
        measureName: change.measureName,
        action: change.approved ? "approved" : "unapproved",
        actedBy: userId,
        actedAt: now,
      });
    }

    const dealIds = [...new Set(changes.map((c) => c.hubspotDealId))];
    const approvalRows = await tx
      .select({
        hubspotDealId: dealMeasureApprovals.hubspotDealId,
        measureName: dealMeasureApprovals.measureName,
      })
      .from(dealMeasureApprovals)
      .where(
        and(
          inArray(dealMeasureApprovals.hubspotDealId, dealIds),
          eq(dealMeasureApprovals.isApproved, true),
        ),
      );

    const approvedMeasuresByDeal: Record<string, string[]> = {};
    for (const row of approvalRows) {
      (approvedMeasuresByDeal[row.hubspotDealId] ??= []).push(row.measureName);
    }
    return { approvedMeasuresByDeal };
  });
};

const defaultSyncApprovals: SyncApprovalsForDeals = async ({
  approvedMeasuresByDeal,
  actedByEmail,
  actedAt,
}) => {
  const dealIds = Object.keys(approvedMeasuresByDeal);
  for (const dealId of dealIds) {
    const measures = approvedMeasuresByDeal[dealId] ?? [];

    void syncMeasureApprovalsToHubSpot({
      hubspotDealId: dealId,
      approvedMeasures: measures.map((m) => ({ measureName: m, approvedByEmail: actedByEmail })),
      actedByEmail,
      actedAt,
    });

    const result = await syncMeasuresFieldToHubSpot({
      hubspotDealId: dealId,
      propName: APPROVED_MEASURES_PROP,
      measureNames: measures,
    });

    if (!result.ok) {
      return { ok: false, error: result.error };
    }
  }
  return { ok: true };
};

export async function bulkApprove(input: BulkApproveInput): Promise<BulkApproveResult> {
  if (input.changes.length === 0) {
    return { ok: false, error: "changes must not be empty" };
  }

  const runTx = input.deps?.runBulkApproveTx ?? defaultRunBulkApproveTx;
  const syncApprovals = input.deps?.syncApprovals ?? defaultSyncApprovals;

  let txResult: BulkApproveTxResult;
  try {
    txResult = await runTx({ changes: input.changes, userId: input.userId });
  } catch (err) {
    const message = err instanceof Error ? err.message : "Bulk approve transaction failed";
    console.error("[bulkApprove] transaction failed", { error: err });
    return { ok: false, error: message };
  }

  const syncResult = await syncApprovals({
    approvedMeasuresByDeal: txResult.approvedMeasuresByDeal,
    actedByEmail: input.actedByEmail ?? "unknown",
    actedAt: new Date(),
  });

  if (!syncResult.ok) {
    return { ok: true, hubspotSync: "failed", hubspotError: syncResult.error };
  }

  return { ok: true, hubspotSync: "ok" };
}
