import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable, inject } from '@angular/core';
import { firstValueFrom } from 'rxjs';
import { deriveWorkspace } from './uat-access.util';

export interface DemoCredential {
  label: string;
  displayName: string;
  username: string;
  email: string;
  password: string;
  clientType: string;
  userType: string;
  branchId: number;
  partyId: number | null;
  roleCodes: string[];
  workspace: string;
  summary: string;
}

export interface DemoBootstrapResponse {
  enabled: boolean;
  provider: string;
  credentials: DemoCredential[];
  notes: string[];
}

export interface LoginResponse {
  accessToken: string;
  refreshToken: string;
  expiresInSeconds: number;
  mfaRequired: boolean;
}

export interface SessionSummary {
  username: string;
  displayName: string;
  workspace: string;
  branchId: number | null;
  partyId: number | null;
  clientType: string;
  roleCodes: string[];
  functionCodes: string[];
}

export interface DownloadedFile {
  blob: Blob;
  fileName: string | null;
  contentType: string | null;
}

interface DesktopAuthState {
  token: string | null;
  session: SessionSummary | null;
}

export interface AuthenticatedUserProfileResponse {
  username: string;
  displayName: string;
  userType: string;
  email: string | null;
  branchId: number | null;
  partyId: number | null;
  workspace: string;
  roleCodes: string[];
  functionCodes: string[];
}

@Injectable({ providedIn: 'root' })
export class CashlineApiService {
  private readonly http = inject(HttpClient);
  private readonly tokenStorageKey = 'cashline-kendo-token';
  private readonly sessionStorageKey = 'cashline-kendo-session';
  private readonly authHintCookieName = 'cashline-kendo-auth';
  private readonly apiBaseUrl = this.resolveApiBaseUrl();
  private token: string | null = this.readStoredToken();
  private session: SessionSummary | null = this.readStoredSession();
  private readonly desktopSecureStore = this.resolveDesktopSecureStore();

  get currentToken(): string | null {
    return this.token;
  }

  get currentSession(): SessionSummary | null {
    return this.session;
  }

  async initialize(): Promise<void> {
    if (!this.desktopSecureStore) {
      return;
    }

    const authState = await this.desktopSecureStore.getAuthState();
    this.token = authState?.token ?? null;
    this.session = authState?.session ?? null;
  }

  async getBootstrap(): Promise<DemoBootstrapResponse> {
    return this.get<DemoBootstrapResponse>('/api/v1/demo/bootstrap', false);
  }

  async login(username: string, password: string, clientType: string): Promise<LoginResponse> {
    const response = await this.post<LoginResponse>('/api/v1/auth/login', {
      username,
      password,
      clientType
    }, false);

    this.token = response.accessToken;
    await this.persistToken(response.accessToken);
    return response;
  }

  async authenticate(username: string, password: string, clientType: string): Promise<SessionSummary> {
    const loginResponse = await this.login(username, password, clientType);
    if (loginResponse.mfaRequired) {
      this.clearToken();
      throw new Error('Additional verification is required before this account can complete sign-in.');
    }

    const profile = await this.get<AuthenticatedUserProfileResponse>('/api/v1/auth/me');

    const session: SessionSummary = {
      username: profile.username,
      displayName: this.normalizeDisplayName(profile.displayName),
      workspace: profile.workspace || deriveWorkspace(profile.roleCodes),
      branchId: profile.branchId ?? null,
      partyId: profile.partyId ?? null,
      clientType,
      roleCodes: profile.roleCodes ?? [],
      functionCodes: profile.functionCodes ?? []
    };

    this.session = session;
    await this.persistSession(session);
    return session;
  }

  clearToken(): void {
    this.token = null;
    this.session = null;
    if (this.desktopSecureStore) {
      void this.desktopSecureStore.clearAuthState();
    }
    if (typeof window !== 'undefined') {
      window.localStorage.removeItem(this.tokenStorageKey);
      window.localStorage.removeItem(this.sessionStorageKey);
      this.clearAuthHintCookie();
    }
  }

  get<T>(path: string, authenticated = true): Promise<T> {
    return firstValueFrom(this.http.get<T>(this.resolveUrl(path), {
      headers: this.buildHeaders(authenticated)
    }));
  }

  post<T>(path: string, body: unknown, authenticated = true): Promise<T> {
    return firstValueFrom(this.http.post<T>(this.resolveUrl(path), body, {
      headers: this.buildHeaders(authenticated)
    }));
  }

  put<T>(path: string, body: unknown, authenticated = true): Promise<T> {
    return firstValueFrom(this.http.put<T>(this.resolveUrl(path), body, {
      headers: this.buildHeaders(authenticated)
    }));
  }

  delete<T>(path: string, body: unknown, authenticated = true): Promise<T> {
    return firstValueFrom(this.http.delete<T>(this.resolveUrl(path), {
      headers: this.buildHeaders(authenticated),
      body
    }));
  }

  postForm<T>(path: string, body: FormData, authenticated = true): Promise<T> {
    return firstValueFrom(this.http.post<T>(this.resolveUrl(path), body, {
      headers: this.buildHeaders(authenticated, true)
    }));
  }

  async download(path: string, authenticated = true): Promise<DownloadedFile> {
    const response = await fetch(this.resolveUrl(path), {
      method: 'GET',
      headers: this.buildFetchHeaders(authenticated)
    });

    if (!response.ok) {
      throw new Error(`Download request failed with status ${response.status}.`);
    }

    const blob = await response.blob();
    const contentDisposition = response.headers.get('Content-Disposition');
    const fileName = this.parseFileName(contentDisposition);
    return {
      blob,
      fileName,
      contentType: response.headers.get('Content-Type')
    };
  }

  private resolveUrl(path: string): string {
    if (path.startsWith('http://') || path.startsWith('https://')) {
      return path;
    }

    return `${this.apiBaseUrl}${path}`;
  }

  private buildHeaders(authenticated: boolean, isFormData = false): HttpHeaders {
    let headers = new HttpHeaders();

    if (!isFormData) {
      headers = headers.set('Content-Type', 'application/json');
    }

    if (authenticated && this.token) {
      headers = headers.set('Authorization', `Bearer ${this.token}`);
    }

    return headers;
  }

  private buildFetchHeaders(authenticated: boolean): Headers {
    const headers = new Headers();
    if (authenticated && this.token) {
      headers.set('Authorization', `Bearer ${this.token}`);
    }

    return headers;
  }

  private resolveApiBaseUrl(): string {
    if (typeof window === 'undefined') {
      return '';
    }

    const desktopConfig = (window as Window & {
      cashlineDesktopConfig?: { apiBaseUrl?: string };
    }).cashlineDesktopConfig;
    if (desktopConfig?.apiBaseUrl) {
      return desktopConfig.apiBaseUrl;
    }

    const globalBaseUrl = (window as Window & { __cashlineApiBaseUrl?: string }).__cashlineApiBaseUrl;
    if (globalBaseUrl) {
      return globalBaseUrl;
    }

    if (window.location.port === '4200') {
      return 'http://127.0.0.1:5080';
    }

    return '';
  }

  private parseFileName(contentDisposition: string | null): string | null {
    if (!contentDisposition) {
      return null;
    }

    const utf8Match = /filename\*=UTF-8''([^;]+)/i.exec(contentDisposition);
    if (utf8Match?.[1]) {
      return decodeURIComponent(utf8Match[1]);
    }

    const plainMatch = /filename="?([^\";]+)"?/i.exec(contentDisposition);
    return plainMatch?.[1] ?? null;
  }

  private readStoredToken(): string | null {
    if (typeof window === 'undefined') {
      return null;
    }

    if (this.resolveDesktopSecureStore()) {
      return null;
    }

    return window.localStorage.getItem(this.tokenStorageKey);
  }

  private readStoredSession(): SessionSummary | null {
    if (typeof window === 'undefined') {
      return null;
    }

    if (this.resolveDesktopSecureStore()) {
      return null;
    }

    const raw = window.localStorage.getItem(this.sessionStorageKey);
    if (!raw) {
      return null;
    }

    try {
      const parsed = JSON.parse(raw) as SessionSummary;
      return {
        ...parsed,
        displayName: this.normalizeDisplayName(parsed.displayName)
      };
    } catch {
      return null;
    }
  }

  private async persistToken(token: string): Promise<void> {
    if (this.desktopSecureStore) {
      await this.desktopSecureStore.setAuthState({
        token,
        session: this.session
      });
      return;
    }

    if (typeof window === 'undefined') {
      return;
    }

    window.localStorage.setItem(this.tokenStorageKey, token);
    this.persistAuthHintCookie();
  }

  private async persistSession(session: SessionSummary): Promise<void> {
    if (this.desktopSecureStore) {
      await this.desktopSecureStore.setAuthState({
        token: this.token,
        session
      });
      return;
    }

    if (typeof window === 'undefined') {
      return;
    }

    window.localStorage.setItem(this.sessionStorageKey, JSON.stringify(session));
    this.persistAuthHintCookie();
  }

  private normalizeDisplayName(displayName: string | null | undefined): string {
    const value = (displayName ?? '').trim();
    if (!value) {
      return 'CashLine User';
    }

    return value
      .replace(/^UAT\s+/i, '')
      .replace(/\s+UAT$/i, '')
      .replace(/\s+User$/i, '')
      .replace(/\s{2,}/g, ' ')
      .trim();
  }

  private persistAuthHintCookie(): void {
    if (typeof document === 'undefined') {
      return;
    }

    document.cookie = `${this.authHintCookieName}=1; Path=${this.resolveAppPrefix()}; Max-Age=86400; SameSite=Lax`;
  }

  private clearAuthHintCookie(): void {
    if (typeof document === 'undefined') {
      return;
    }

    document.cookie = `${this.authHintCookieName}=; Path=${this.resolveAppPrefix()}; Max-Age=0; SameSite=Lax`;
  }

  private resolveAppPrefix(): string {
    if (typeof window === 'undefined') {
      return '/cutover';
    }

    const segments = window.location.pathname.split('/').filter(Boolean);
    return segments.length > 0 ? `/${segments[0]}` : '/cutover';
  }

  private resolveDesktopSecureStore():
    | {
        getAuthState(): Promise<DesktopAuthState | null>;
        setAuthState(authState: DesktopAuthState): Promise<void>;
        clearAuthState(): Promise<void>;
      }
    | null {
    if (typeof window === 'undefined') {
      return null;
    }

    return (window as Window & {
      cashlineDesktopSecureStore?: {
        getAuthState(): Promise<DesktopAuthState | null>;
        setAuthState(authState: DesktopAuthState): Promise<void>;
        clearAuthState(): Promise<void>;
      };
    }).cashlineDesktopSecureStore ?? null;
  }
}
