import { APIRequestContext, Browser, BrowserContext, Page } from '@playwright/test';

type AuthRole = 'auth' | 'admin' | 'sponsor';

type Credentials = {
  email: string;
  password: string;
};

export interface AuthContext {
  context: BrowserContext;
  page: Page;
  role: AuthRole;
}

export class WebsiteAuthManager {
  static async createAuthContext(browser: Browser, role: AuthRole): Promise<AuthContext>;
  static async createAuthContext(browser: Browser, request: APIRequestContext, role: AuthRole): Promise<AuthContext>;
  static async createAuthContext(
    browser: Browser,
    requestOrRole: APIRequestContext | AuthRole,
    maybeRole?: AuthRole,
  ): Promise<AuthContext> {
    const baseURL = process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:3000';
    const apiBaseUrl = process.env.API_BASE_URL || 'http://localhost:3101';

    const role = (typeof requestOrRole === 'string' ? requestOrRole : maybeRole) as AuthRole;

    // If using API login, create context with cookies pre-set
    if (typeof requestOrRole !== 'string') {
      const token = await WebsiteAuthManager.loginViaApi(requestOrRole, apiBaseUrl, role);
      
      console.log(`[WebsiteAuthManager] Creating context with pre-set cookie, baseURL: ${baseURL}, token length: ${token.length}`);
      
      // Create context with storage state that includes the cookie
      // This is more reliable than adding cookies after context creation
      const contextWithCookies = await browser.newContext({
        baseURL,
        storageState: {
          cookies: [
            {
              name: 'gp_session',
              value: token,
              domain: new URL(baseURL).hostname,
              path: '/',
              expires: -1,
              httpOnly: true,
              secure: false,
              sameSite: 'Lax',
            },
          ],
          origins: [],
        },
      });
      
      const page = await contextWithCookies.newPage();
      
      // Verify cookies
      const cookies = await contextWithCookies.cookies();
      console.log(`[WebsiteAuthManager] Cookies in context:`, cookies);
      
      return {
        context: contextWithCookies,
        page,
        role,
      };
    }

    // UI login path
    const context = await browser.newContext({ baseURL });
    const page = await context.newPage();
    await WebsiteAuthManager.loginViaUi(page, role);

    return {
      context,
      page,
      role,
    };
  }

  private static async loginViaApi(
    request: APIRequestContext,
    apiBaseUrl: string,
    role: AuthRole,
  ): Promise<string> {
    const credentials = WebsiteAuthManager.getCredentials(role);

    // In Docker, the API is at http://api:3000, but the website needs to receive cookies
    // that will be forwarded to the API. The cookie domain should match the website.
    const res = await request.post(`${apiBaseUrl}/auth/login`, {
      data: {
        email: credentials.email,
        password: credentials.password,
      },
    });

    const setCookie = res.headers()['set-cookie'] ?? '';
    const cookiePart = setCookie.split(';')[0] ?? '';
    const token = cookiePart.startsWith('gp_session=') ? cookiePart.slice('gp_session='.length) : '';

    if (!token) {
      throw new Error(`Expected gp_session cookie from ${apiBaseUrl}/auth/login`);
    }

    return token;
  }

  private static async loginViaUi(page: Page, role: AuthRole): Promise<void> {
    const credentials = WebsiteAuthManager.getCredentials(role);

    await page.goto('/auth/login');
    await page.getByLabel('Email Address').fill(credentials.email);
    await page.getByLabel('Password').fill(credentials.password);

    await Promise.all([
      page.getByRole('button', { name: 'Sign In' }).click(),
      page.waitForURL((url) => !url.pathname.startsWith('/auth/login'), { timeout: 15_000 }),
    ]);
  }

  private static getCredentials(role: AuthRole): Credentials {
    if (role === 'admin') {
      return { email: 'demo.admin@example.com', password: 'Demo1234!' };
    }

    if (role === 'sponsor') {
      return { email: 'demo.sponsor@example.com', password: 'Demo1234!' };
    }

    return { email: 'demo.driver@example.com', password: 'Demo1234!' };
  }
}