import PocketBase from 'pocketbase';
import type { AuthConfig, AuthState, AuthCallbacks } from './types';

/**
 * PocketBase OAuth2 Frontend Module
 * Handles user authentication and token management
 */
export class PocketBaseAuth {
  private pb: PocketBase;
  private config: Required<AuthConfig>;
  private callbacks: AuthCallbacks;
  private state: AuthState;

  constructor(config: AuthConfig, callbacks?: Partial<AuthCallbacks>) {
    this.config = {
      pbUrl: config.pbUrl!,
      collection: config.collection || 'Users',
      provider: config.provider || 'microsoft',
      loginContainerId: config.loginContainerId || 'loginContainer',
      userDisplayNameId: config.userDisplayNameId || 'userDisplayName',
      userEmailId: config.userEmailId || 'userEmailValue',
      loginBtnId: config.loginBtnId || 'loginBtn',
      loginErrorId: config.loginErrorId || 'loginError',
    };

    this.callbacks = {
      onAuthSuccess: callbacks?.onAuthSuccess || (() => {}),
      onAuthFailure: callbacks?.onAuthFailure || (() => {}),
      onTokenUpdate: callbacks?.onTokenUpdate || (() => {}),
      onUiUpdate: callbacks?.onUiUpdate || (() => {}),
    };

    this.pb = new PocketBase(this.config.pbUrl);
    this.state = {
      isAuthenticated: false,
      user: null,
      token: null,
    };

    this.init();
  }

  /**
   * Initialize auth module
   */
  private init(): void {
    this.updateAuthUI();
    if (this.pb.authStore.isValid && this.pb.authStore.token) {
      this.ensureUserLogged();
    }
    this.setupLoginButton();
  }

  /**
   * Setup login button click handler
   */
  private setupLoginButton(): void {
    const loginBtn = document.getElementById(this.config.loginBtnId);
    if (!loginBtn) {
      console.warn(`Login button with id "${this.config.loginBtnId}" not found`);
      return;
    }

    loginBtn.addEventListener('click', async (e) => {
      e.preventDefault();
      await this.handleLogin();
    });
  }

  /**
   * Handle login button click
   */
  private async handleLogin(): Promise<void> {
    const loginBtn = document.getElementById(this.config.loginBtnId) as HTMLButtonElement;
    const loginError = document.getElementById(this.config.loginErrorId) as HTMLElement;

    if (!loginBtn || !loginError) return;

    loginBtn.disabled = true;
    loginBtn.textContent = 'Checking session...';
    loginError.classList.add('hidden');

    try {
      // Try to use existing token first
      const hadToken = await this.ensureUserLogged();
      if (hadToken) {
        this.resetLoginButton();
        return;
      }

      // Otherwise perform OAuth
      loginBtn.textContent = 'Logging in...';
      const authData = await this.pb.collection(this.config.collection).authWithOAuth2({
        provider: this.config.provider,
      });

      this.updateState(authData);
      this.updateAuthUI();
      this.callbacks.onAuthSuccess?.(this.state);

      console.log('✓ Logged in with', this.config.provider);
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Unknown error';
      console.error('Login failed:', message);
      loginError.textContent = `Login failed: ${message}`;
      loginError.classList.remove('hidden');
      this.callbacks.onAuthFailure?.(error);
    } finally {
      this.resetLoginButton();
    }
  }

  /**
   * Ensure user is logged in (check existing token)
   */
  async ensureUserLogged(): Promise<boolean> {
    if (!this.pb.authStore.isValid || !this.pb.authStore.token) {
      return false;
    }

    try {
      const refresh = await this.pb.collection(this.config.collection).authRefresh();
      this.updateState(refresh);
      this.updateAuthUI();
      this.callbacks.onTokenUpdate?.(this.state);
      console.log('✓ Token refreshed');
      return true;
    } catch (error) {
      console.warn('Existing token invalid, clearing auth');
      this.pb.authStore.clear();
      this.updateState(null);
      this.updateAuthUI();
      return false;
    }
  }

  /**
   * Update internal auth state
   */
  private updateState(data: any): void {
    if (!data) {
      this.state = {
        isAuthenticated: false,
        user: null,
        token: null,
      };
      return;
    }

    const record = data.record || this.pb.authStore.record || this.pb.authStore.model;
    const meta = data.meta || {};
    const model = this.pb.authStore.model;

    this.state = {
      isAuthenticated: true,
      user: {
        id: record?.id || model?.id || 'unknown',
        name: record?.name || model?.name || meta?.name || record?.email || model?.email || 'Unknown User',
        email: record?.email || model?.email || meta?.email || '(no email)',
      },
      token: this.pb.authStore.token,
    };
  }

  /**
   * Update UI based on auth state
   */
  updateAuthUI(): void {
    const loginContainer = document.getElementById(this.config.loginContainerId);
    const userDisplayName = document.getElementById(this.config.userDisplayNameId);
    const userEmail = document.getElementById(this.config.userEmailId);

    if (!loginContainer) return;

    if (this.pb.authStore.isValid) {
      loginContainer.classList.add('hidden');
      if (this.state.user) {
        if (userDisplayName) userDisplayName.textContent = this.state.user.name;
        if (userEmail) userEmail.textContent = this.state.user.email;
      }
    } else {
      loginContainer.classList.remove('hidden');
      const loginError = document.getElementById(this.config.loginErrorId);
      if (loginError) loginError.classList.add('hidden');
    }

    this.callbacks.onUiUpdate?.(this.state);
  }

  /**
   * Check token status (for verification/display)
   */
  async checkTokenStatus(): Promise<{ pbToken: boolean; tokenExpiry?: string }> {
    const pbToken = !!this.pb.authStore.token;
    return { pbToken };
  }

  /**
   * Get current auth state
   */
  getAuthState(): AuthState {
    return { ...this.state };
  }

  /**
   * Get PocketBase instance (for direct usage if needed)
   */
  getPocketBase(): PocketBase {
    return this.pb;
  }

  /**
   * Logout
   */
  logout(): void {
    this.pb.authStore.clear();
    this.updateState(null);
    this.updateAuthUI();
    console.log('✓ Logged out');
  }

  /**
   * Reset login button to initial state
   */
  private resetLoginButton(): void {
    const loginBtn = document.getElementById(this.config.loginBtnId) as HTMLButtonElement;
    if (loginBtn) {
      loginBtn.disabled = false;
      loginBtn.textContent = 'Login with Microsoft';
    }
  }
}

/**
 * Quick init function - fetches config from backend
 */
export async function initPocketBaseAuth(
  backendUrl: string,
  callbacks?: Partial<AuthCallbacks>
): Promise<PocketBaseAuth> {
  // Fetch frontend config from backend
  const response = await fetch(`${backendUrl}/api/auth/config`);
  if (!response.ok) {
    throw new Error('Failed to fetch auth configuration from backend');
  }
  const config = await response.json();
  
  const auth = new PocketBaseAuth(config, callbacks);
  return auth;
}
