import os from "node:os";
import path from "node:path";
import fs from "node:fs";

// Env + a throwaway DB must be in place before ../db is first imported (it
// creates the schema on import), so this runs ahead of the other imports.
const dbDir = fs.mkdtempSync(path.join(os.tmpdir(), "eatme-attendance-test-"));
process.env.DB_PATH = path.join(dbDir, "test.db");
process.env.GOOGLE_CLIENT_ID = "test-client-id";
process.env.ADMIN_EMAILS = "admin@example.com";
process.env.JWT_SECRET = "test-secret";

import { db } from "../db";
import { eventsInRange, getLiveStatus, summarize } from "./attendance";

function makeEmployee(email: string): number {
  const result = db.prepare("INSERT INTO employees (email, name) VALUES (?, ?)").run(email, email);
  return Number(result.lastInsertRowid);
}

function insertEvent(employeeId: number, type: string, ts: string): void {
  db.prepare("INSERT INTO attendance_events (employee_id, type, ts) VALUES (?, ?, ?)").run(employeeId, type, ts);
}

describe("forgotten clock-out is capped automatically at 12h", () => {
  test("a shift open more than 12h is auto-closed at clock_in + 12h", () => {
    const alice = makeEmployee("alice@example.com");
    const clockIn = new Date(Date.now() - 20 * 60 * 60 * 1000).toISOString(); // 20h ago
    insertEvent(alice, "clock_in", clockIn);

    expect(getLiveStatus(alice)).toBe("clocked_out");

    const events = db
      .prepare("SELECT * FROM attendance_events WHERE employee_id = ? ORDER BY ts ASC")
      .all(alice) as { type: string; ts: string }[];
    expect(events.map((e) => e.type)).toEqual(["clock_in", "clock_out"]);
    expect(new Date(events[1].ts).getTime() - new Date(clockIn).getTime()).toBe(12 * 60 * 60 * 1000);
  });

  test("a shift left open on a break is also closed cleanly (break_end inserted first)", () => {
    const bob = makeEmployee("bob@example.com");
    const clockIn = new Date(Date.now() - 15 * 60 * 60 * 1000).toISOString();
    const breakStart = new Date(Date.now() - 14 * 60 * 60 * 1000).toISOString();
    insertEvent(bob, "clock_in", clockIn);
    insertEvent(bob, "break_start", breakStart);

    expect(getLiveStatus(bob)).toBe("clocked_out");

    const events = db
      .prepare("SELECT * FROM attendance_events WHERE employee_id = ? ORDER BY ts ASC")
      .all(bob) as { type: string }[];
    expect(events.map((e) => e.type)).toEqual(["clock_in", "break_start", "break_end", "clock_out"]);
  });

  test("a shift within 12h is left untouched", () => {
    const carol = makeEmployee("carol@example.com");
    const clockIn = new Date(Date.now() - 3 * 60 * 60 * 1000).toISOString();
    insertEvent(carol, "clock_in", clockIn);

    expect(getLiveStatus(carol)).toBe("working");
  });

  test("closure/stats reads (eventsInRange) also self-heal a stale shift", () => {
    const dave = makeEmployee("dave@example.com");
    const clockIn = new Date(Date.now() - 30 * 60 * 60 * 1000).toISOString(); // 30h ago
    insertEvent(dave, "clock_in", clockIn);

    const events = eventsInRange(dave, "2000-01-01T00:00:00.000Z", "2100-01-01T00:00:00.000Z");
    const { sessions } = summarize(events, new Date());
    expect(sessions).toHaveLength(1);
    expect(sessions[0].open).toBe(false);
    expect(sessions[0].workedMs).toBe(12 * 60 * 60 * 1000);
  });
});
