import { describe, expect, it } from "vitest";
import { fromDbJst, jstIsoFromDb, toDbJst, toJstIso } from "@/lib/format/date";

describe("toJstIso", () => {
  it("UTC 0時 → JST 9時（日付は変わらない）", () => {
    expect(toJstIso(new Date("2026-08-27T00:00:00.000Z"))).toBe(
      "2026-08-27T09:00:00+09:00"
    );
  });

  it("UTC 15時 → JST 翌0時（日付が繰り上がる）", () => {
    expect(toJstIso(new Date("2026-08-27T15:00:00.000Z"))).toBe(
      "2026-08-28T00:00:00+09:00"
    );
  });

  it("秒未満は切り捨てられる（DBがTimestamp(0)/DateTime(0)なので通常は0秒）", () => {
    expect(toJstIso(new Date("2026-08-27T00:00:00.500Z"))).toBe(
      "2026-08-27T09:00:00+09:00"
    );
  });

  it("元のJST文字列をnew Date()でparseしてから戻すと一致する（往復確認）", () => {
    const original = "2026-08-27T02:00:00+09:00";
    expect(toJstIso(new Date(original))).toBe(original);
  });
});

// ★指示書Y — DBのDATETIMEは日本時間の壁時計値（TeamWorks第8回A2）。
//   Prismaのアダプタはそれを UTC とみなして Date にするため、読み出しの境界で
//   fromDbJst() を通して「真の瞬間」に直す。表記の約束: 「DBの生値」は
//   new Date(`<値>Z`) （＝アダプタの実際の挙動と同じ形）で入力を書く★
describe("fromDbJst", () => {
  it("基本形（深夜清掃）: DB生値 2026-09-11 02:00:00 → 真の瞬間 JST 02:00", () => {
    expect(fromDbJst(new Date("2026-09-11T02:00:00Z")).getTime()).toBe(
      new Date("2026-09-11T02:00:00+09:00").getTime()
    );
  });

  it("日をまたぐ（JST 00:30）", () => {
    expect(fromDbJst(new Date("2026-09-11T00:30:00Z")).getTime()).toBe(
      new Date("2026-09-11T00:30:00+09:00").getTime()
    );
  });

  it("日をまたぐ（JST 23:30）", () => {
    expect(fromDbJst(new Date("2026-09-11T23:30:00Z")).getTime()).toBe(
      new Date("2026-09-11T23:30:00+09:00").getTime()
    );
  });

  it("月をまたぐ（月末の深夜）", () => {
    expect(fromDbJst(new Date("2026-08-31T23:30:00Z")).getTime()).toBe(
      new Date("2026-08-31T23:30:00+09:00").getTime()
    );
  });

  it("月境界（UTCの日付で月を判定してはいけないことの証拠）", () => {
    expect(fromDbJst(new Date("2026-09-01T00:30:00Z")).getTime()).toBe(
      new Date("2026-09-01T00:30:00+09:00").getTime()
    );
  });

  it("うるう年", () => {
    expect(fromDbJst(new Date("2024-02-29T02:00:00Z")).getTime()).toBe(
      new Date("2024-02-29T02:00:00+09:00").getTime()
    );
  });

  it("年またぎ", () => {
    expect(fromDbJst(new Date("2027-01-01T00:30:00Z")).getTime()).toBe(
      new Date("2027-01-01T00:30:00+09:00").getTime()
    );
  });

  it("ミリ秒が保たれる（情報を落とさない）", () => {
    expect(fromDbJst(new Date("2026-09-11T02:00:00.500Z")).getTime()).toBe(
      new Date("2026-09-11T02:00:00.500+09:00").getTime()
    );
  });
});

describe("toDbJst", () => {
  it("基本形: 真の瞬間 → DBに書きたい壁時計値をUTCフィールドに載せたDate", () => {
    expect(toDbJst(new Date("2026-09-11T02:00:00+09:00")).getTime()).toBe(
      new Date("2026-09-11T02:00:00Z").getTime()
    );
  });

  it("往復（fromDbJst(toDbJst(d)) === d）", () => {
    const inputs = [
      "2026-09-11T02:00:00+09:00",
      "2026-09-11T00:30:00+09:00",
      "2026-09-11T23:30:00+09:00",
      "2026-08-31T23:30:00+09:00",
      "2026-09-01T00:30:00+09:00",
      "2024-02-29T02:00:00+09:00",
      "2027-01-01T00:30:00+09:00",
      "2026-09-11T02:00:00.500+09:00",
    ];
    for (const s of inputs) {
      const d = new Date(s);
      expect(fromDbJst(toDbJst(d)).getTime()).toBe(d.getTime());
    }
  });

  it("逆向きの往復（toDbJst(fromDbJst(d)) === d）", () => {
    const inputs = [
      "2026-09-11T02:00:00Z",
      "2026-09-11T00:30:00Z",
      "2026-09-11T23:30:00Z",
      "2026-08-31T23:30:00Z",
      "2026-09-01T00:30:00Z",
      "2024-02-29T02:00:00Z",
      "2027-01-01T00:30:00Z",
      "2026-09-11T02:00:00.500Z",
    ];
    for (const s of inputs) {
      const d = new Date(s);
      expect(toDbJst(fromDbJst(d)).getTime()).toBe(d.getTime());
    }
  });
});

// ★jstIsoFromDb() は toJstIso(fromDbJst(date)) の合成。戻り値の桁はDBの生値そのものになる
//   ことが「直った」ことの判定基準（指示書Y）★
describe("jstIsoFromDb", () => {
  it("DBの生値の桁がそのまま出る（これが本書の判定基準）", () => {
    expect(jstIsoFromDb(new Date("2026-09-11T02:00:00Z"))).toBe(
      "2026-09-11T02:00:00+09:00"
    );
  });

  it("日付が前日に落ちない", () => {
    expect(jstIsoFromDb(new Date("2026-09-11T00:30:00Z"))).toBe(
      "2026-09-11T00:30:00+09:00"
    );
  });

  it("月が変わらない", () => {
    expect(jstIsoFromDb(new Date("2026-08-31T23:30:00Z"))).toBe(
      "2026-08-31T23:30:00+09:00"
    );
  });

  it("月が変わらない（逆方向）", () => {
    expect(jstIsoFromDb(new Date("2026-09-01T00:30:00Z"))).toBe(
      "2026-09-01T00:30:00+09:00"
    );
  });

  it("うるう年", () => {
    expect(jstIsoFromDb(new Date("2024-02-29T02:00:00Z"))).toBe(
      "2024-02-29T02:00:00+09:00"
    );
  });

  it("年またぎ", () => {
    expect(jstIsoFromDb(new Date("2027-01-01T00:30:00Z"))).toBe(
      "2027-01-01T00:30:00+09:00"
    );
  });

  it("プロパティ: 任意のDB生値文字列について、DBの生値と表示が一致する（一般化）", () => {
    const dbValues = [
      "2026-09-11 02:00:00",
      "2026-09-11 00:30:00",
      "2026-09-11 23:30:00",
      "2026-08-31 23:30:00",
      "2026-09-01 00:30:00",
      "2024-02-29 02:00:00",
      "2027-01-01 00:30:00",
      "2026-01-01 00:00:00",
      "2026-12-31 23:59:59",
    ];
    for (const s of dbValues) {
      const isoNoOffset = s.replace(" ", "T");
      const result = jstIsoFromDb(new Date(`${isoNoOffset}Z`));
      expect(result).toBe(`${isoNoOffset}+09:00`);
    }
  });
});
