#!/usr/bin/env bun
/**
 * AnyClip Authentication Script
 *
 * Automates login to AnyClip and returns session credentials.
 *
 * Usage:
 *   bun scripts/auth.ts <email> <password>
 *
 * Or import and use programmatically:
 *   import { login, getAuthHeaders } from './scripts/auth';
 */

// Load .env file
import { file } from "bun";
const envFile = file(".env");
if (await envFile.exists()) {
  const envContent = await envFile.text();
  for (const line of envContent.split("\n")) {
    const [key, ...valueParts] = line.split("=");
    if (key && valueParts.length) {
      process.env[key.trim()] = valueParts.join("=").trim();
    }
  }
}

import { encryptString } from "./crypto-subtle";

const PASS_CRYPTO_SALT = "$2b$04$wwky7rvtr6BFNaCqntwyie";
const EXTERNAL_API = "https://videomanager-api.anyclip.com";
const MAIN_API = "https://videomanager.anyclip.com";

export interface LoginResponse {
  cookieName: string;
  cookieValue: string;
  token: string;
  user: string; // base64 encoded JSON
}

export interface AuthSession {
  cookies: string; // Both cookies combined
  anyclipCookie: string;
  sessionCookie: string;
  token: string;
  user: DecodedUser;
}

export interface DecodedUser {
  id: number;
  firstName: string;
  lastName: string;
  email?: string;
  roleId: number;
  accountId: number;
  role: {
    id: number;
    name: string;
    displayName: string;
    type: string;
  };
  account: {
    id: number;
    name: string;
    publisherId?: number;
    publishers?: Array<{ id: number; name: string }>;
  };
  permissions: string[];
  publisherIds: number[];
  publisherId: number;
  slug: string;
}

/**
 * Encrypt password using AnyClip's client-side encryption (SubtleCrypto)
 */
export async function encryptPassword(password: string): Promise<string> {
  return encryptString(password, PASS_CRYPTO_SALT);
}

/**
 * Step 1: Login to external API
 */
async function loginExternal(
  email: string,
  password: string
): Promise<LoginResponse> {
  const encryptedPassword = await encryptPassword(password);

  const response = await fetch(`${EXTERNAL_API}/public/auth/login`, {
    method: "POST",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      email,
      password: encryptedPassword,
    }),
  });

  if (!response.ok) {
    const error = await response.text();
    throw new Error(`External login failed: ${response.status} - ${error}`);
  }

  return response.json();
}

/**
 * Step 2: Login to main app (returns session cookie)
 */
async function loginMain(
  token: string,
  cookieName: string,
  cookieValue: string
): Promise<string> {
  const response = await fetch(`${MAIN_API}/api/auth/login`, {
    method: "POST",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      token,
      tcname: cookieName,
      tcvalue: cookieValue,
    }),
  });

  if (!response.ok) {
    const error = await response.text();
    throw new Error(`Main login failed: ${response.status} - ${error}`);
  }

  // Extract session cookie from Set-Cookie header
  const setCookies = response.headers.getSetCookie?.() || [];
  const sessionCookie = setCookies
    .find((c) => c.startsWith("session="))
    ?.split(";")[0];

  if (!sessionCookie) {
    throw new Error("No session cookie returned from main login");
  }

  return sessionCookie;
}

/**
 * Decode the base64-encoded user object
 */
export function decodeUser(base64User: string): DecodedUser {
  const json = Buffer.from(base64User, "base64").toString("utf-8");
  return JSON.parse(json);
}

/**
 * Full login flow - returns session credentials
 */
export async function login(
  email: string,
  password: string
): Promise<AuthSession> {
  // Step 1: External API login
  const externalResponse = await loginExternal(email, password);
  const anyclipCookie = `${externalResponse.cookieName}=${externalResponse.cookieValue}`;

  // Step 2: Main app login - get session cookie
  const sessionCookie = await loginMain(
    externalResponse.token,
    externalResponse.cookieName,
    externalResponse.cookieValue
  );

  // Decode user
  const user = decodeUser(externalResponse.user);

  return {
    cookies: `${anyclipCookie}; ${sessionCookie}`,
    anyclipCookie,
    sessionCookie,
    token: externalResponse.token,
    user,
  };
}

/**
 * Get headers for authenticated API requests
 */
export function getAuthHeaders(session: AuthSession): Record<string, string> {
  return {
    Accept: "application/json",
    "Content-Type": "application/json",
    Cookie: session.cookies,
  };
}

/**
 * Make an authenticated GraphQL request
 */
export async function graphqlRequest<T = unknown>(
  session: AuthSession,
  query: string,
  variables: Record<string, unknown> = {}
): Promise<T> {
  const response = await fetch(`${MAIN_API}/api/graphql`, {
    method: "POST",
    headers: getAuthHeaders(session),
    body: JSON.stringify({ query, variables }),
  });

  if (!response.ok) {
    const error = await response.text();
    throw new Error(`GraphQL request failed: ${response.status} - ${error}`);
  }

  const result = await response.json();
  if (result.errors) {
    throw new Error(`GraphQL errors: ${JSON.stringify(result.errors)}`);
  }

  return result.data;
}

// CLI usage
if (import.meta.main) {
  const [emailArg, passwordArg] = process.argv.slice(2);

  const email =
    emailArg || process.env.ANYCLIP_EMAIL || process.env.ANYCLIP_USER;
  const password = passwordArg || process.env.ANYCLIP_PASSWORD;

  if (!email || !password) {
    console.error("Usage: bun scripts/auth.ts <email> <password>");
    console.error("   Or: set ANYCLIP_EMAIL and ANYCLIP_PASSWORD env vars");
    process.exit(1);
  }

  try {
    console.log("Logging in...");
    const session = await login(email, password);

    console.log("\n✅ Login successful!\n");
    console.log("User:", session.user.firstName, session.user.lastName);
    console.log("Account:", session.user.account?.name);
    console.log("Role:", session.user.role?.displayName);
    console.log("\nCookies:", session.cookies.substring(0, 80) + "...");
    console.log("\nJWT Token:", session.token.substring(0, 50) + "...");

    // Save session to file for other scripts
    const sessionFile = "session.json";
    await Bun.write(
      sessionFile,
      JSON.stringify(
        {
          cookies: session.cookies,
          anyclipCookie: session.anyclipCookie,
          sessionCookie: session.sessionCookie.substring(0, 100) + "...",
          token: session.token,
          user: {
            id: session.user.id,
            name: `${session.user.firstName} ${session.user.lastName}`,
            accountId: session.user.accountId,
            publisherId: session.user.publisherId,
          },
        },
        null,
        2
      )
    );
    console.log(`\nSession saved to ${sessionFile}`);
  } catch (error) {
    console.error("Login failed:", error);
    process.exit(1);
  }
}
