/**
 * Authentication helpers for E2E tests
 * Provides consistent login/logout functionality
 */

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

const RAW_API_BASE =
  process.env.PLAYWRIGHT_API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000';
const RESOLVED_API_BASE =
  RAW_API_BASE.startsWith('http://') || RAW_API_BASE.startsWith('https://')
    ? RAW_API_BASE
    : 'http://localhost:8000';
const API_BASE = RESOLVED_API_BASE.endsWith('/api') ? RESOLVED_API_BASE : `${RESOLVED_API_BASE}/api`;
const TOKEN_STORAGE_KEY = 'arqera-auth-tokens';
const DEFAULT_CREDENTIALS = {
  email: process.env.E2E_USER_EMAIL || 'admin@arqera.ai',
  password: process.env.E2E_USER_PASSWORD || 'Admin123!Change',
};

export interface AuthCredentials {
  email: string;
  password: string;
}

export interface AuthState {
  accessToken: string;
  refreshToken?: string | null;
  expiresAt?: number | null;
  user: {
    id: string;
    email: string;
    name: string;
  };
}

export const AuthHelpers = {
  /**
   * Login via the UI login form
   */
  async loginViaUI(page: Page, credentials: AuthCredentials): Promise<void> {
    await page.goto('/auth/login');

    // Fill login form
    const emailInput = page.locator('input[type="email"], input[name="email"]');
    const passwordInput = page.locator('input[type="password"], input[name="password"]');
    const submitButton = page.locator('button[type="submit"]');

    await emailInput.fill(credentials.email);
    await passwordInput.fill(credentials.password);
    await submitButton.click();

    // Wait for redirect or success
    await page.waitForURL((url) => !url.pathname.includes('/login'), { timeout: 10000 });
  },

  /**
   * Login via API and store auth state
   */
  async loginViaAPI(page: Page, credentials: AuthCredentials): Promise<AuthState | null> {
    const response = await page.request.post(`${API_BASE}/auth/login`, {
      data: {
        email: credentials.email,
        password: credentials.password,
      },
    });

    if (!response.ok()) {
      return null;
    }

    const authState = await response.json();
    const accessToken = authState.access_token || authState.accessToken;
    if (!accessToken) {
      return null;
    }
    const refreshToken = authState.refresh_token || authState.refreshToken || null;
    const expiresInRaw = authState.expires_in ?? authState.expiresIn ?? null;
    const expiresIn = expiresInRaw ? Number(expiresInRaw) : 3600;
    const user = {
      id: String(authState.user_id || authState.user?.id || 'e2e-user'),
      email: authState.email || authState.user?.email || credentials.email,
      name: authState.full_name || authState.user?.name || 'E2E User',
    };

    if (page.url() === 'about:blank') {
      await page.goto('/');
    }
    await this.setStoredTokens(page, {
      accessToken,
      refreshToken,
      expiresInSeconds: expiresIn,
    });

    return {
      accessToken,
      refreshToken,
      expiresAt: expiresIn ? Date.now() + Number(expiresIn) * 1000 : null,
      user,
    };
  },

  /**
   * Logout via UI
   */
  async logoutViaUI(page: Page): Promise<void> {
    // Look for logout button in user menu
    const userMenu = page.locator('[data-testid="user-menu"], button:has-text("Account"), [aria-label*="user"]');

    if (await userMenu.isVisible()) {
      await userMenu.click();
      await page.waitForTimeout(300);
    }

    const logoutButton = page.locator('button:has-text("Logout"), button:has-text("Sign out"), a:has-text("Logout")');

    if (await logoutButton.isVisible()) {
      await logoutButton.click();
      await page.waitForURL((url) => url.pathname.includes('/auth/login'), { timeout: 10000 });
    }
  },

  /**
   * Clear auth state
   */
  async clearAuthState(page: Page): Promise<void> {
    await page.evaluate(() => {
      localStorage.removeItem('arqera-auth-tokens');
      localStorage.removeItem('accessToken');
      localStorage.removeItem('refreshToken');
      localStorage.removeItem('user');
      sessionStorage.clear();
    });
  },

  /**
   * Check if user is authenticated
   */
  async isAuthenticated(page: Page): Promise<boolean> {
    const token = await this.getAuthToken(page);
    try {
      const response = await page.request.get(`${API_BASE}/auth/me`, {
        headers: token ? { Authorization: `Bearer ${token}` } : undefined,
      });
      if (response.ok()) {
        return true;
      }
    } catch {
      // ignore and fall back to token presence
    }
    return !!token;
  },

  /**
   * Get stored auth token
   */
  async getAuthToken(page: Page): Promise<string | null> {
    return page.evaluate(() => {
      const readToken = (storage: Storage | null) => {
        if (!storage) return null;
        const raw = storage.getItem('arqera-auth-tokens');
        if (!raw) return null;
        try {
          const parsed = JSON.parse(raw);
          return parsed?.state?.tokens?.accessToken || null;
        } catch {
          return null;
        }
      };
      const token =
        readToken(typeof localStorage !== 'undefined' ? localStorage : null) ||
        readToken(typeof sessionStorage !== 'undefined' ? sessionStorage : null);
      if (token) return token;
      if (typeof document === 'undefined') return null;
      const cookie = document.cookie
        .split(';')
        .map((entry) => entry.trim())
        .find((entry) => entry.startsWith('access_token='));
      return cookie ? cookie.split('=')[1] ?? null : null;
    });
  },

  /**
   * Setup authenticated state for tests that need it
   */
  async setupAuthenticatedState(context: BrowserContext, credentials: AuthCredentials): Promise<void> {
    const page = await context.newPage();

    try {
      // Try API login first for speed
      const authState = await this.loginViaAPI(page, credentials);

      if (!authState) {
        // Fallback to UI login
        await this.loginViaUI(page, credentials);
      }
    } finally {
      await page.close();
    }
  },

  /**
   * Mock authenticated state (for testing without real auth)
   */
  async mockAuthenticatedState(page: Page): Promise<void> {
    if (page.url() === 'about:blank') {
      await page.goto('/');
    }

    const loginState = await this.loginViaAPI(page, DEFAULT_CREDENTIALS);
    if (loginState) {
      return;
    }

    await this.setStoredTokens(page, {
      accessToken: 'mock-access-token-for-testing',
      refreshToken: 'mock-refresh-token-for-testing',
      expiresInSeconds: 3600,
    });
    await this.stubAuthMe(page, {
      id: 'mock-user-id',
      email: 'test@example.com',
      name: 'Test User',
    });
  },

  /**
   * Wait for auth to be ready
   */
  async waitForAuth(page: Page, timeout = 5000): Promise<boolean> {
    const startTime = Date.now();

    while (Date.now() - startTime < timeout) {
      const isAuth = await this.isAuthenticated(page);
      if (isAuth) return true;
      await page.waitForTimeout(100);
    }

    return false;
  },

  async setStoredTokens(
    page: Page,
    params: { accessToken: string; refreshToken?: string | null; expiresInSeconds?: number | null }
  ): Promise<void> {
    const expiresAt = params.expiresInSeconds
      ? Date.now() + Number(params.expiresInSeconds) * 1000
      : null;
    const payload = {
      state: {
        tokens: {
          accessToken: params.accessToken,
          refreshToken: params.refreshToken ?? null,
          expiresAt,
        },
      },
      version: 0,
    };
    await page.evaluate(
      ({ key, value }) => {
        localStorage.setItem(key, JSON.stringify(value));
        sessionStorage.setItem(key, JSON.stringify(value));
      },
      { key: TOKEN_STORAGE_KEY, value: payload }
    );
    const base = page.url().startsWith('http')
      ? page.url()
      : process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:3000';
    try {
      const url = new URL(base);
      const expiresAtSeconds = expiresAt ? Math.floor(expiresAt / 1000) : undefined;
      await page.context().addCookies([
        {
          name: 'access_token',
          value: params.accessToken,
          url: url.origin,
          path: '/',
          httpOnly: false,
          sameSite: 'Lax',
          expires: expiresAtSeconds,
        },
        ...(params.refreshToken
          ? [
              {
                name: 'refresh_token',
                value: params.refreshToken,
                url: url.origin,
                path: '/',
                httpOnly: false,
                sameSite: 'Lax',
                expires: expiresAtSeconds,
              },
            ]
          : []),
      ]);
    } catch {
      // Non-fatal: storage is enough for most browsers.
    }
  },

  async stubAuthMe(
    page: Page,
    user: { id: string; email: string; name: string; role?: string; actor_type?: string; onboarding_completed?: boolean }
  ): Promise<void> {
    await page.unroute('**/api/auth/me');
    await page.route('**/api/auth/me', (route) => {
      route.fulfill({
        status: 200,
        contentType: 'application/json',
        body: JSON.stringify({
          user_id: Number.isNaN(Number(user.id)) ? 1 : Number(user.id),
          email: user.email,
          full_name: user.name,
          actor_type: user.actor_type || 'HUMAN',
          role: user.role || 'member',
          onboarding_completed: user.onboarding_completed ?? true,
          email_verified: true,
        }),
      });
    });
  },
};

export default AuthHelpers;
