import { writeFileSync, mkdirSync } from "fs";

mkdirSync("lib", { recursive: true });

// ─── lib/api.ts ─────────────────────────────────────────────────────────────
writeFileSync("lib/api.ts", `// Client-side fetch helper with automatic Bearer token injection and 401 auto-refresh.

export interface ApiResponse<T = unknown> {
  data: T;
  meta: { timestamp: string; version: "v1" };
  error: null | { message: string; code?: string };
}

const TOKEN_KEY = "ledgerone_token";
const REFRESH_KEY = "ledgerone_refresh_token";
const USER_KEY = "ledgerone_user";

export function getStoredToken(): string {
  if (typeof window === "undefined") return "";
  return localStorage.getItem(TOKEN_KEY) ?? "";
}

export function getStoredUser<T = unknown>(): T | null {
  if (typeof window === "undefined") return null;
  try {
    const raw = localStorage.getItem(USER_KEY);
    return raw ? (JSON.parse(raw) as T) : null;
  } catch {
    return null;
  }
}

export function storeAuthTokens(data: {
  accessToken: string;
  refreshToken: string;
  user: unknown;
}): void {
  localStorage.setItem(TOKEN_KEY, data.accessToken);
  localStorage.setItem(REFRESH_KEY, data.refreshToken);
  localStorage.setItem(USER_KEY, JSON.stringify(data.user));
  // Set a non-HttpOnly cookie so Next.js middleware can detect auth state
  document.cookie = "ledgerone_auth=1; path=/; max-age=2592000; SameSite=Lax";
}

export function clearAuth(): void {
  localStorage.removeItem(TOKEN_KEY);
  localStorage.removeItem(REFRESH_KEY);
  localStorage.removeItem(USER_KEY);
  document.cookie = "ledgerone_auth=; path=/; max-age=0";
}

async function tryRefresh(): Promise<string | null> {
  const refreshToken = localStorage.getItem(REFRESH_KEY);
  if (!refreshToken) return null;
  try {
    const res = await fetch("/api/auth/refresh", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ refreshToken }),
    });
    if (!res.ok) {
      clearAuth();
      return null;
    }
    const payload = (await res.json()) as ApiResponse<{
      accessToken: string;
      refreshToken: string;
    }>;
    if (payload.error || !payload.data?.accessToken) {
      clearAuth();
      return null;
    }
    localStorage.setItem(TOKEN_KEY, payload.data.accessToken);
    localStorage.setItem(REFRESH_KEY, payload.data.refreshToken);
    return payload.data.accessToken;
  } catch {
    clearAuth();
    return null;
  }
}

export async function apiFetch<T = unknown>(
  path: string,
  options: RequestInit = {}
): Promise<ApiResponse<T>> {
  const token = getStoredToken();
  const headers: Record<string, string> = {
    ...(options.headers as Record<string, string>),
  };
  if (token) headers["Authorization"] = \`Bearer \${token}\`;
  if (
    options.body &&
    typeof options.body === "string" &&
    !headers["Content-Type"]
  ) {
    headers["Content-Type"] = "application/json";
  }

  let res = await fetch(path, { ...options, headers });

  // Auto-refresh on 401
  if (res.status === 401) {
    const newToken = await tryRefresh();
    if (newToken) {
      headers["Authorization"] = \`Bearer \${newToken}\`;
      res = await fetch(path, { ...options, headers });
    } else {
      if (typeof window !== "undefined") {
        window.location.href = "/login";
      }
      return {
        data: null as T,
        meta: { timestamp: new Date().toISOString(), version: "v1" },
        error: { message: "Session expired. Please sign in again." },
      };
    }
  }

  return res.json() as Promise<ApiResponse<T>>;
}
`);

// ─── lib/backend.ts ──────────────────────────────────────────────────────────
writeFileSync("lib/backend.ts", `// Server-side proxy helper for Next.js API routes.
// Forwards requests to the NestJS backend, including the Bearer token.

import { NextRequest, NextResponse } from "next/server";

const BASE_URL = process.env.LEDGERONE_API_URL ?? "http://localhost:3051";

export function getBackendUrl(path: string): string {
  return \`\${BASE_URL}/api/\${path.replace(/^\\//, "")}\`;
}

interface ProxyOptions {
  method?: string;
  body?: BodyInit | null;
  extraHeaders?: Record<string, string>;
  search?: string;
}

export async function proxyRequest(
  req: NextRequest,
  backendPath: string,
  options: ProxyOptions = {}
): Promise<NextResponse> {
  const url = new URL(req.url);
  const search = options.search !== undefined ? options.search : url.search;
  const targetUrl = \`\${getBackendUrl(backendPath)}\${search}\`;

  const method = options.method ?? req.method;
  const auth = req.headers.get("authorization") ?? "";
  const contentType = req.headers.get("content-type") ?? "";

  const headers: Record<string, string> = { ...options.extraHeaders };
  if (auth) headers["Authorization"] = auth;

  let body: BodyInit | null | undefined = undefined;

  if (method !== "GET" && method !== "HEAD") {
    if (options.body !== undefined) {
      body = options.body;
      if (contentType) headers["Content-Type"] = contentType;
    } else if (contentType.includes("multipart/form-data")) {
      body = await req.formData();
      // Do not set Content-Type — fetch sets it with boundary automatically
    } else {
      body = await req.text();
      if (contentType) headers["Content-Type"] = contentType;
    }
  }

  try {
    const res = await fetch(targetUrl, {
      method,
      headers,
      body: body ?? undefined,
    });
    const payload = await res.text();
    return new NextResponse(payload, {
      status: res.status,
      headers: {
        "Content-Type": res.headers.get("content-type") ?? "application/json",
      },
    });
  } catch {
    return NextResponse.json(
      {
        data: null,
        meta: { timestamp: new Date().toISOString(), version: "v1" },
        error: { message: "Backend unavailable." },
      },
      { status: 503 }
    );
  }
}
`);

// ─── middleware.ts ───────────────────────────────────────────────────────────
writeFileSync("middleware.ts", `import { NextRequest, NextResponse } from "next/server";

const PROTECTED_PREFIXES = [
  "/app",
  "/transactions",
  "/rules",
  "/exports",
  "/tax",
  "/settings",
  "/profile",
];

export function middleware(req: NextRequest) {
  const { pathname } = req.nextUrl;

  const isProtected = PROTECTED_PREFIXES.some(
    (prefix) => pathname === prefix || pathname.startsWith(\`\${prefix}/\`)
  );

  if (!isProtected) return NextResponse.next();

  // ledgerone_auth cookie is set by storeAuthTokens() in lib/api.ts
  const authCookie = req.cookies.get("ledgerone_auth");
  if (!authCookie?.value) {
    const loginUrl = new URL("/login", req.url);
    loginUrl.searchParams.set("next", pathname);
    return NextResponse.redirect(loginUrl);
  }

  return NextResponse.next();
}

export const config = {
  matcher: [
    // Run on all routes except Next.js internals and static files
    "/((?!_next/static|_next/image|favicon\\.ico|api/).*)",
  ],
};
`);

console.log("✅ lib/api.ts, lib/backend.ts, middleware.ts written");
