import { config as loadDotenv } from "dotenv";
import { existsSync } from "node:fs";
import path from "node:path";

// Ladda .env från paketet ELLER monorepo-roten (pnpm --filter sätter cwd till paketet).
for (const candidate of [".env", "../.env", "../../.env"]) {
  const p = path.resolve(process.cwd(), candidate);
  if (existsSync(p)) {
    loadDotenv({ path: p });
    break;
  }
}
import { createDatabase, type Database } from "@app/database";
import { createAamosClient, type AamosClient, type BudgetStore } from "@app/ai-contracts";
import { createStorageService, type StorageService } from "@app/storage";
import type { Redis } from "ioredis";

export interface WorkerContext {
  db: Database;
  aamos: AamosClient;
  redis?: Redis;
  /** Delad dagsbudget-store så att memory-jobbet kan spegla skanningens budgetkoll. */
  budgetStore?: BudgetStore;
  /** Lagringstjänst för att läsa/skriva bilder (S3 eller mock). */
  storage: StorageService;
  /** Bygger läs-URL för lagrade bilder (presignerad S3 i aws-läge, mock-URL lokalt). */
  readUrl: (key: string) => Promise<string>;
  apiBaseUrl: string;
  close: () => Promise<void>;
}

class RedisBudgetStore implements BudgetStore {
  constructor(
    private readonly redis: Redis,
    private readonly key: string,
  ) {}

  private alarmKey(): string {
    return this.key.replace(/^gemini:daily:budget:/, "gemini:daily:budget:alarm:");
  }

  async getDailySpendUsd(): Promise<number> {
    const val = await this.redis.get(this.key);
    return val ? Number(val) : 0;
  }

  async incrementDailySpendUsd(amountUsd: number): Promise<number> {
    const newVal = await this.redis.incrbyfloat(this.key, amountUsd);
    // Expire at next midnight UTC to keep daily window.
    const ttl = this.secondsUntilMidnightUtc();
    await this.redis.expire(this.key, ttl);
    return Number(newVal);
  }

  async raiseBudgetAlarm(): Promise<void> {
    const ttl = this.secondsUntilMidnightUtc();
    await this.redis.set(this.alarmKey(), "1", "EX", ttl);
  }

  private secondsUntilMidnightUtc(): number {
    const now = new Date();
    const midnight = new Date(
      Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1),
    );
    return Math.max(1, Math.floor((midnight.getTime() - now.getTime()) / 1000));
  }
}

function requireEnv(name: string): string {
  const value = process.env[name];
  if (!value) throw new Error(`Missing ${name}. Worker must be configured explicitly.`);
  return value;
}

export function createContext(redis?: Redis): WorkerContext {
  const databaseUrl = requireEnv("DATABASE_URL");
  const { db, pool } = createDatabase(databaseUrl);
  const budgetStore = redis
    ? new RedisBudgetStore(redis, `gemini:daily:budget:${new Date().toISOString().slice(0, 10)}`)
    : undefined;
  const aamos = createAamosClient(undefined, { budgetStore });
  const apiBaseUrl = process.env.API_BASE_URL ?? "http://localhost:4000";
  const signingSecret = requireEnv("ENTITLEMENT_SIGNING_SECRET");

  const s3Mode = process.env.S3_MODE === "aws" ? "aws" : "mock";
  const storage = createStorageService({
    mode: s3Mode,
    baseUrl: apiBaseUrl,
    signingSecret,
    s3Bucket: process.env.S3_BUCKET ?? "",
    s3Region: process.env.S3_REGION ?? "",
    s3Endpoint: process.env.S3_ENDPOINT || undefined,
    s3AccessKeyId: process.env.S3_ACCESS_KEY_ID || undefined,
    s3SecretAccessKey: process.env.S3_SECRET_ACCESS_KEY || undefined,
  });

  return {
    db,
    aamos,
    redis,
    budgetStore,
    storage,
    apiBaseUrl,
    readUrl: (key: string) => storage.getReadUrl(key),
    close: async () => {
      await pool.end();
    },
  };
}
