// ── Config Session DO — handles addon config sessions via fetch() dispatch ──
import type { ConfigSession, CreateSessionRequest } from "./models";

const SESSION_TTL_MS = 15 * 60 * 1000;

export class ConfigSessionDO implements DurableObject {
  private state: DurableObjectState;
  private storage: DurableObjectStorage;

  constructor(ctx: DurableObjectState, _env: unknown) {
    this.state = ctx;
    this.storage = ctx.storage;
  }

  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);
    const path = url.pathname;

    try {
      if (path === "/create" && request.method === "POST") {
        const body = await request.json() as CreateSessionRequest;
        return this.handleCreate(body);
      }

      if (path === "/status") {
        return this.handleGetStatus();
      }

      if (path === "/complete" && request.method === "POST") {
        const body = await request.json() as { configuredManifestUrl?: string };
        return this.handleComplete(body.configuredManifestUrl || "");
      }

      if (path === "/activate") {
        const s = await this.storage.get<ConfigSession>("session");
        if (s && s.status === "pending") { s.status = "active"; await this.storage.put("session", s); }
        return json(s || { error: "Not found" }, s ? 200 : 404);
      }

      if (path === "/cancel") {
        const s = await this.storage.get<ConfigSession>("session");
        if (s) { s.status = "cancelled"; await this.storage.put("session", s); }
        return json({ status: "cancelled" });
      }

      return json({ error: "Not found" }, 404);
    } catch (e) {
      return json({ error: (e as Error).message }, 500);
    }
  }

  private async handleCreate(body: CreateSessionRequest): Promise<Response> {
    if (!body.code || !body.addonId || !body.addonName || !body.baseManifestUrl)
      return json({ error: "Missing fields" }, 400);

    const now = Date.now();
    const session: ConfigSession = {
      code: body.code, addonId: body.addonId, addonName: body.addonName,
      baseManifestUrl: body.baseManifestUrl, configureUrl: body.configureUrl || null,
      status: "pending", configuredManifestUrl: null,
      validatedManifestOk: null, validationError: null,
      createdAt: now, expiresAt: now + SESSION_TTL_MS,
    };
    await this.storage.put("session", session);
    await this.storage.setAlarm(SESSION_TTL_MS);
    return json(session, 201);
  }

  private async handleGetStatus(): Promise<Response> {
    const s = await this.storage.get<ConfigSession>("session");
    if (!s) return json({ error: "Not found", status: "cancelled" }, 404);
    if (Date.now() > s.expiresAt && s.status !== "completed") {
      s.status = "cancelled"; await this.storage.put("session", s);
    }
    return json(s);
  }

  private async handleComplete(configuredManifestUrl: string): Promise<Response> {
    if (!configuredManifestUrl) return json({ error: "Missing configuredManifestUrl" }, 400);

    const s = await this.storage.get<ConfigSession>("session");
    if (!s || s.status === "cancelled") return json({ error: "Not found" }, 404);

    // Validate the manifest URL
    try {
      const resp = await fetch(configuredManifestUrl, {
        headers: { "User-Agent": "Unspooled-Config/1.0", Accept: "application/json" },
      });
      if (resp.ok) {
        const body = await resp.json() as any;
        s.validatedManifestOk = !!(body.id && body.name);
        if (!s.validatedManifestOk) s.validationError = "Invalid manifest: missing id or name";
        else s.manifestName = body.name as string;
      } else {
        s.validatedManifestOk = false;
        s.validationError = "HTTP " + resp.status;
      }
    } catch (e) {
      s.validatedManifestOk = false;
      s.validationError = (e as Error).message;
    }

    s.configuredManifestUrl = configuredManifestUrl;
    s.status = s.validatedManifestOk ? "completed" : "error";
    await this.storage.put("session", s);
    return json(s);
  }

  async alarm(): Promise<void> {
    const s = await this.storage.get<ConfigSession>("session");
    if (s && (s.status === "pending" || s.status === "active")) {
      s.status = "cancelled"; await this.storage.put("session", s);
    }
  }
}

function json(data: unknown, status = 200): Response {
  return new Response(JSON.stringify(data), {
    status, headers: { "Content-Type": "application/json" },
  });
}
