/**
 * Prisma クライアントのシングルトン。
 *
 * ★このファイルを `lib/db/index.ts` の先頭で静的 import しないこと★
 *   一部のテスト（`lib/settings/__tests__/advice.test.ts` 等）が `@/lib/db` を import するため、
 *   静的 import にすると Prisma クライアント未生成の環境（`USE_FIXTURES=true` の
 *   通常の開発者を含む）で `npm test` が全滅する。`prismaSource` の各メソッドの中で
 *   `await import("./prisma")` して遅延読み込みすること（指示書M 段階1-4 / 3-2）。
 *
 * ★`globalThis` にキャッシュする理由★
 *   `next dev` は編集のたびにモジュールを作り直すため、素直に `new PrismaClient()` すると
 *   接続が増え続ける。後任が「普通に import すればいいのに」と直してしまわないよう、
 *   ここに理由を書き残す。
 */
import { databaseWriteCredentials } from "@/lib/config/env-check";
import type { PrismaClient } from "@/lib/generated/prisma/client";

declare global {
  var __dwatchPrisma: PrismaClient | undefined;
  /** 書き込み用アカウントの接続（`getWritePrisma()`。指示書AT §3-2） */
  var __dwatchPrismaWrite: PrismaClient | undefined;
}

/**
 * Prisma 7 は driver adapter が必須（`datasource.url` だけでは接続できない）。
 * MySQL/MariaDB 用は `@prisma/adapter-mariadb`（インストール済み。バージョンは
 * `@prisma/client` と揃える。指示書M 段階1 で判明した追加事項）。
 */
/**
 * アダプタ用の接続情報を組み立てる。
 *
 * ★パスワードに `@` が含まれるとき、`DATABASE_URL` の `%40` が dotenv 展開で壊れることがある★
 *   （`p%40ss` → `p` + 空の `%40ss`）。その場合は部品変数を優先する。
 * ★リモート DB は connectTimeout を延ばす★（既定 ~1s だと pool timeout になる）
 *
 * ★★ユーザー名に既定値を置かないこと★★ — 実在のDBユーザー名をコードに書くのは
 *   `CLAUDE.md` §9.3「認証情報を絶対にコミットしない」に反する。
 *   2026-09-10 のコミット `c832891` でテストから同じ値を除去した経緯がある。
 *   未設定なら黙って既定値で繋ぎにいかず、★何が足りないかを言って落とす★。
 */
function mariadbAdapterConfig(PrismaMariaDb: typeof import("@prisma/adapter-mariadb").PrismaMariaDb) {
  const host = process.env.DATABASE_HOST;
  const password = process.env.DATABASE_PASSWORD;
  if (host && password !== undefined) {
    const user = process.env.DATABASE_USER;
    const database = process.env.DATABASE_NAME;
    const missing = [
      user ? null : "DATABASE_USER",
      database ? null : "DATABASE_NAME",
    ].filter((name): name is string => name !== null);
    if (missing.length > 0) {
      throw new Error(
        `[db] DATABASE_HOST で接続する場合は ${missing.join(" と ")} も必要です。` +
          "環境変数（Docker の env_file・.env・.env.local など）を確認してください（実値はコードに書かないこと）。"
      );
    }
    return new PrismaMariaDb({
      host,
      port: Number(process.env.DATABASE_PORT || "3306"),
      user,
      password,
      database,
      connectTimeout: 30_000,
    });
  }
  const databaseUrl = process.env.DATABASE_URL;
  if (!databaseUrl) {
    throw new Error(
      "[db] DATABASE_URL（または DATABASE_HOST + DATABASE_PASSWORD）が設定されていません。環境変数（Docker の env_file・.env・.env.local など）を確認してください。"
    );
  }
  return new PrismaMariaDb(databaseUrl);
}

export async function getPrisma(): Promise<PrismaClient> {
  if (!globalThis.__dwatchPrisma) {
    const [{ PrismaClient: PrismaClientCtor }, { PrismaMariaDb }] = await Promise.all([
      import("@/lib/generated/prisma/client"),
      import("@prisma/adapter-mariadb"),
    ]);
    const adapter = mariadbAdapterConfig(PrismaMariaDb);
    globalThis.__dwatchPrisma = new PrismaClientCtor({ adapter });
  }
  return globalThis.__dwatchPrisma;
}

/**
 * 書き込み用の接続（指示書AT §3-2）。★メインDBへの書き込み（`lib/db/robot-error-write.ts`）だけが使う★。
 *
 * - `DATABASE_WRITE_USER` / `DATABASE_WRITE_PASSWORD` が★2つとも未設定★ → ★`getPrisma()` と同じクライアント★
 *   （参照用アカウントで書く。いまはこれで動く見込み）
 * - ★片方だけ★ → ★例外★（黙って参照用に倒さない）。メッセージに値を出さない
 * - ★2つとも設定★ → 別の接続を作る。★接続先（ホスト・ポート・DB名）は参照用と同じ DB★なので
 *   `DATABASE_HOST` / `DATABASE_PORT` / `DATABASE_NAME` を使う（★URL 形式は設けない★ —
 *   `%40` が壊れる問題（`mariadbAdapterConfig()` の注記）を持ち込まないため）
 *
 * ★`globalThis.__dwatchPrismaWrite` にキャッシュ★（`getPrisma()` と同じ理由）。
 * 判定の基準は `databaseWriteCredentials()`（`lib/config/env-check.ts`。`npm run check:env` と同じ1本）。
 */
export async function getWritePrisma(): Promise<PrismaClient> {
  const state = databaseWriteCredentials(process.env);
  if (state === "none") return getPrisma();
  if (state === "partial") {
    throw new Error(
      "[db] DATABASE_WRITE_USER と DATABASE_WRITE_PASSWORD は両方設定してください（片方だけでは書き込みません）。" +
        "値はこのエラーには出しません。"
    );
  }
  if (!globalThis.__dwatchPrismaWrite) {
    const host = process.env.DATABASE_HOST;
    const database = process.env.DATABASE_NAME;
    const missing = [
      host ? null : "DATABASE_HOST",
      database ? null : "DATABASE_NAME",
    ].filter((name): name is string => name !== null);
    if (missing.length > 0) {
      throw new Error(
        `[db] 書き込み用アカウント（DATABASE_WRITE_USER）を使う場合は ${missing.join(" と ")} も必要です。` +
          "環境変数（Docker の env_file・.env・.env.local など）を確認してください（実値はコードに書かないこと）。"
      );
    }
    const [{ PrismaClient: PrismaClientCtor }, { PrismaMariaDb }] = await Promise.all([
      import("@/lib/generated/prisma/client"),
      import("@prisma/adapter-mariadb"),
    ]);
    const adapter = new PrismaMariaDb({
      host,
      port: Number(process.env.DATABASE_PORT || "3306"),
      user: process.env.DATABASE_WRITE_USER,
      password: process.env.DATABASE_WRITE_PASSWORD,
      database,
      connectTimeout: 30_000,
    });
    globalThis.__dwatchPrismaWrite = new PrismaClientCtor({ adapter });
  }
  return globalThis.__dwatchPrismaWrite;
}
