import type { DatabaseClient } from "./database";

type ReleasableDistribution = {
  id: string;
  task_id: string;
  task_type: string;
  content_id: string;
  content_title: string;
  partner_id: string;
  partner_name: string;
  claim_id: string | null;
  delegation_bundle_id: string | null;
  publish_url: string | null;
  result_submitted_at: string | null;
};

export class DistributionReleaseError extends Error {
  readonly status: number;

  constructor(message: string, status: number) {
    super(message);
    this.name = "DistributionReleaseError";
    this.status = status;
  }
}

export function distributionReleaseBlockReason(input: {
  publishUrl?: string | null;
  resultSubmittedAt?: string | null;
  taskType?: string | null;
}) {
  if (input.publishUrl) return "已回填发布链接的笔记不能释放";
  if (input.resultSubmittedAt) return "已提交结果截图的任务不能释放";
  return null;
}

export async function releaseUnfinishedDistribution(
  database: DatabaseClient,
  distributionId: string,
) {
  if (!distributionId) {
    throw new DistributionReleaseError("请选择需要释放的领取记录", 400);
  }

  return database.transaction(async (db) => {
    const distribution = await db
      .prepare(
        `SELECT
          d.id,
          d.task_id,
          t.task_type,
          d.content_id,
          c.title AS content_title,
          d.partner_id,
          p.name AS partner_name,
          d.claim_id,
          d.delegation_bundle_id,
          d.publish_url,
          d.result_submitted_at
         FROM distributions d
         JOIN tasks t ON t.id = d.task_id
         JOIN contents c ON c.id = d.content_id
         JOIN partners p ON p.id = d.partner_id
         WHERE d.id = ?
         FOR UPDATE`,
      )
      .bind(distributionId)
      .first<ReleasableDistribution>();

    if (!distribution) {
      throw new DistributionReleaseError("领取记录不存在或已被释放", 404);
    }
    const blocked = distributionReleaseBlockReason({
      publishUrl: distribution.publish_url,
      resultSubmittedAt: distribution.result_submitted_at,
      taskType: distribution.task_type,
    });
    if (blocked) throw new DistributionReleaseError(blocked, 409);

    await db
      .prepare("DELETE FROM collection_runs WHERE distribution_id = ?")
      .bind(distribution.id)
      .run();
    await db
      .prepare("DELETE FROM distributions WHERE id = ?")
      .bind(distribution.id)
      .run();
    await db
      .prepare("UPDATE contents SET status = 'available' WHERE id = ?")
      .bind(distribution.content_id)
      .run();
    await db
      .prepare(
        `UPDATE tasks
         SET claimed_quantity = GREATEST(claimed_quantity - 1, 0)
         WHERE id = ?`,
      )
      .bind(distribution.task_id)
      .run();
    await db
      .prepare(
        `UPDATE partners
         SET claimed_total = GREATEST(claimed_total - 1, 0)
         WHERE id = ?`,
      )
      .bind(distribution.partner_id)
      .run();

    if (distribution.delegation_bundle_id) {
      await db
        .prepare(
          `UPDATE delegation_bundles
           SET quantity = GREATEST(quantity - 1, 0),
               status = CASE WHEN quantity <= 1 THEN 'revoked' ELSE status END,
               revoked_at = CASE WHEN quantity <= 1 THEN CURRENT_TIMESTAMP ELSE revoked_at END,
               updated_at = CURRENT_TIMESTAMP
           WHERE id = ?`,
        )
        .bind(distribution.delegation_bundle_id)
        .run();
    }

    if (distribution.claim_id) {
      await db
        .prepare(
          `UPDATE claims
           SET quantity = GREATEST(quantity - 1, 0)
           WHERE id = ?`,
        )
        .bind(distribution.claim_id)
        .run();
      await db
        .prepare(
          `DELETE FROM claims
           WHERE id = ?
             AND quantity <= 0
             AND NOT EXISTS (
               SELECT 1 FROM distributions WHERE claim_id = ?
             )`,
        )
        .bind(distribution.claim_id, distribution.claim_id)
        .run();
    }

    return {
      distributionId: distribution.id,
      taskId: distribution.task_id,
      contentId: distribution.content_id,
      contentTitle: distribution.content_title,
      partnerName: distribution.partner_name,
    };
  });
}
