// usage.ts
// Scope: local limited-tier recorder usage tracking.
// Counts only accepted recorder manifest executions, not chat, logs, reads, proposals, or settings.

import fs from "node:fs";
import type { UserTier } from "./auth";
import { getLimits } from "./tiers";
import { USAGE_PATH } from "../pathList";

const NORMAL_LIMIT = 5;
const BONUS_EXTRA = 5;
const FIRST_BONUS_DELAY_DAYS = 7;
const BONUS_WINDOW_MIN_DAYS = 10;
const BONUS_WINDOW_MAX_DAYS = 15;

type UsageState = {
  installDate: string;
  currentDate: string;
  recordRunsToday: number;
  lastBonusDate: string;
  nextBonusDate: string;
  bonusNoticeShownDate: string;
};

export type UsageDecision = {
  ok: boolean;
  tier: UserTier;
  limit: number;
  used: number;
  remaining: number;
  bonusActive: boolean;
  code?: string;
  message?: string;
};

function todayKey(): string {
  return new Date().toISOString().slice(0, 10);
}

function addDays(dateKey: string, days: number): string {
  const d = new Date(`${dateKey}T00:00:00.000Z`);
  d.setUTCDate(d.getUTCDate() + days);
  return d.toISOString().slice(0, 10);
}

function randomWindowDays(): number {
  return BONUS_WINDOW_MIN_DAYS + Math.floor(Math.random() * (BONUS_WINDOW_MAX_DAYS - BONUS_WINDOW_MIN_DAYS + 1));
}

function scheduleFirstBonus(installDate: string): string {
  return addDays(installDate, FIRST_BONUS_DELAY_DAYS + randomWindowDays());
}

function scheduleNextBonus(fromDate: string): string {
  return addDays(fromDate, randomWindowDays());
}

function defaultState(): UsageState {
  const today = todayKey();
  return {
    installDate: today,
    currentDate: today,
    recordRunsToday: 0,
    lastBonusDate: "",
    nextBonusDate: scheduleFirstBonus(today),
    bonusNoticeShownDate: ""
  };
}

function readState(): UsageState {
  try {
    if (!fs.existsSync(USAGE_PATH)) return defaultState();
    const parsed = JSON.parse(fs.readFileSync(USAGE_PATH, "utf8"));
    const base = defaultState();
    return {
      installDate: String(parsed.installDate || base.installDate),
      currentDate: String(parsed.currentDate || base.currentDate),
      recordRunsToday: Number(parsed.recordRunsToday || 0),
      lastBonusDate: String(parsed.lastBonusDate || ""),
      nextBonusDate: String(parsed.nextBonusDate || base.nextBonusDate),
      bonusNoticeShownDate: String(parsed.bonusNoticeShownDate || "")
    };
  } catch {
    return defaultState();
  }
}

function writeState(state: UsageState): void {
  fs.writeFileSync(USAGE_PATH, JSON.stringify(state, null, 2), "utf8");
}

function normalizeState(): UsageState {
  const state = readState();
  const today = todayKey();

  if (!state.nextBonusDate) state.nextBonusDate = scheduleFirstBonus(state.installDate || today);

  if (state.currentDate !== today) {
    state.currentDate = today;
    state.recordRunsToday = 0;
    state.bonusNoticeShownDate = "";
  }

  writeState(state);
  return state;
}

function isBonusDay(state: UsageState): boolean {
  const today = todayKey();
  return Boolean(state.nextBonusDate && state.nextBonusDate === today);
}

function currentLimitFor(state: UsageState): number {
  return isBonusDay(state) ? NORMAL_LIMIT + BONUS_EXTRA : NORMAL_LIMIT;
}

export function checkRecordRunAllowed(tier: UserTier): UsageDecision {
  const limits = getLimits(tier);
  if (limits.runsPerDay === -1) {
    return { ok: true, tier, limit: -1, used: 0, remaining: -1, bonusActive: false };
  }

  const state = normalizeState();
  const bonusActive = isBonusDay(state);
  const limit = currentLimitFor(state);
  const used = state.recordRunsToday;
  const remaining = Math.max(0, limit - used);

  if (used >= limit) {
    return {
      ok: false,
      tier,
      limit,
      used,
      remaining: 0,
      bonusActive,
      code: bonusActive ? "limited_bonus_runs_exhausted" : "limited_daily_record_limit_reached",
      message: bonusActive
        ? "You used all 10 RECORD runs for today, including your bonus runs. Upgrade to Standard for unlimited RECORD runs and launch sooner."
        : "You used your 5 RECORD runs for today. Upgrade to Standard for unlimited RECORD runs, more projects, and privacy abstraction."
    };
  }

  if (bonusActive && used >= NORMAL_LIMIT) {
    const firstBonusNoticeToday = state.bonusNoticeShownDate !== state.currentDate;
    if (firstBonusNoticeToday) {
      state.bonusNoticeShownDate = state.currentDate;
      writeState(state);
    }
    return {
      ok: true,
      tier,
      limit,
      used,
      remaining,
      bonusActive: true,
      code: firstBonusNoticeToday ? "limited_bonus_runs_granted" : "limited_bonus_run_available",
      message: firstBonusNoticeToday
        ? "You used your 5 RECORD runs today. Bonus day: keep going — you have 5 more today. Imagine how much sooner you could launch with unlimited runs."
        : "Bonus runs are active today. Keep going — every finished change gets you closer to launch."
    };
  }

  return { ok: true, tier, limit, used, remaining, bonusActive };
}

export function commitRecordRun(tier: UserTier): UsageDecision {
  const limits = getLimits(tier);
  if (limits.runsPerDay === -1) {
    return { ok: true, tier, limit: -1, used: 0, remaining: -1, bonusActive: false };
  }

  const state = normalizeState();
  const bonusActive = isBonusDay(state);
  const limit = currentLimitFor(state);

  state.recordRunsToday = Math.min(limit, state.recordRunsToday + 1);

  if (bonusActive && state.recordRunsToday >= limit) {
    state.lastBonusDate = state.currentDate;
    state.nextBonusDate = scheduleNextBonus(state.currentDate);
  }

  writeState(state);

  return {
    ok: true,
    tier,
    limit,
    used: state.recordRunsToday,
    remaining: Math.max(0, limit - state.recordRunsToday),
    bonusActive,
    code: state.recordRunsToday === NORMAL_LIMIT && !bonusActive ? "limited_daily_record_warning" : undefined,
    message: state.recordRunsToday === NORMAL_LIMIT && !bonusActive
      ? "You used your 5 RECORD runs for today. Upgrade to Standard for unlimited runs and keep building toward launch."
      : undefined
  };
}

export function getUsageSummary(tier: UserTier): UsageDecision {
  const limits = getLimits(tier);
  if (limits.runsPerDay === -1) {
    return { ok: true, tier, limit: -1, used: 0, remaining: -1, bonusActive: false };
  }
  const state = normalizeState();
  const limit = currentLimitFor(state);
  return {
    ok: true,
    tier,
    limit,
    used: state.recordRunsToday,
    remaining: Math.max(0, limit - state.recordRunsToday),
    bonusActive: isBonusDay(state)
  };
}
