import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { environment } from '../../environments/environment';
import { BehaviorSubject } from 'rxjs';
import { tap } from 'rxjs/operators';

export interface RegisterPayload {
  name: string;
  email: string;
  phone: string;
  password: string;
  confirmPassword: string;
}

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

export interface LoginOptions {
  rememberMe?: boolean;
}

export interface LoginResponse {
  token?: string;
  accessToken?: string;
}

export interface AuthUserProfile {
  id: string;
  nome: string;
  email: string;
  tenantId: string;
  roles: string[];
}

@Injectable({ providedIn: 'root' })
export class AuthService {
  private baseUrl = `${environment.apiUrl}/auth`;
  private userProfileSubject = new BehaviorSubject<AuthUserProfile | null>(null);
  readonly userProfile$ = this.userProfileSubject.asObservable();
  private readonly tokenStorageKey = 'token';
  private readonly tokenExpiresAtKey = 'tokenExpiresAt';
  private readonly rememberMeHours = 6;

  constructor(private http: HttpClient) {
    this.syncUserProfileFromToken();
  }

  register(payload: RegisterPayload) {
    return this.http.post<{ token: string }>(`${this.baseUrl}/register`, payload)
      .pipe(tap(r => this.setToken(r.token)));
  }

  login(payload: LoginPayload, options?: LoginOptions) {
    return this.http.post<LoginResponse>(`${this.baseUrl}/login`, payload)
      .pipe(
        tap((r) => {
          const token = this.resolveLoginToken(r);
          if (!token) return;
          this.setToken(token, options?.rememberMe ?? false);
        })
      );
  }

  logout() {
    if (typeof window === 'undefined') {
      this.userProfileSubject.next(null);
      return;
    }

    this.clearTokenStorage(localStorage);
    this.clearTokenStorage(sessionStorage);
    this.userProfileSubject.next(null);
  }

  setToken(token: string, rememberMe = false) {
    if (typeof window === 'undefined') return;
    this.clearTokenStorage(localStorage);
    this.clearTokenStorage(sessionStorage);

    if (rememberMe) {
      const expiresAt = Date.now() + this.rememberMeHours * 60 * 60 * 1000;
      localStorage.setItem(this.tokenStorageKey, token);
      localStorage.setItem(this.tokenExpiresAtKey, String(expiresAt));
    } else {
      sessionStorage.setItem(this.tokenStorageKey, token);
    }

    this.syncUserProfileFromToken();
  }

  get token(): string | null {
    if (typeof window === 'undefined') return null;
    this.cleanupExpiredRememberSession();

    const sessionToken = sessionStorage.getItem(this.tokenStorageKey);
    if (sessionToken) return sessionToken;

    return localStorage.getItem(this.tokenStorageKey);
  }

  isLoggedIn(): boolean {
    return !!this.token;
  }

  get currentUserProfile(): AuthUserProfile | null {
    return this.userProfileSubject.value;
  }

  syncUserProfileFromToken() {
    this.userProfileSubject.next(this.buildProfileFromToken());
  }

  updateUserProfile(profile: Pick<AuthUserProfile, 'nome' | 'email'>) {
    const current = this.userProfileSubject.value;
    if (!current) return;

    this.userProfileSubject.next({
      ...current,
      nome: profile.nome.trim(),
      email: profile.email.trim().toLowerCase(),
    });
  }

  getTokenPayload(): Record<string, any> | null {
    const token = this.token;
    if (!token) return null;
    try {
      const payload = token.split('.')[1];
      const base64 = payload.replace(/-/g, '+').replace(/_/g, '/');
      const json = decodeURIComponent(
        atob(base64)
          .split('')
          .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
          .join('')
      );
      return JSON.parse(json);
    } catch {
      return null;
    }
  }

  getRoles(): string[] {
    const payload = this.getTokenPayload();
    if (!payload) return [];
    return this.extractRoles(payload);
  }

  private extractRoles(payload: Record<string, any>): string[] {
    const possibleKeys = [
      'role',
      'roles',
      'http://schemas.microsoft.com/ws/2008/06/identity/claims/role',
    ];
    let roles: string[] = [];
    for (const key of possibleKeys) {
      const value = payload[key];
      if (!value) continue;
      if (Array.isArray(value)) roles = roles.concat(value);
      else roles.push(String(value));
    }
    return roles.map(r => r.toLowerCase());
  }

  private buildProfileFromToken(): AuthUserProfile | null {
    const payload = this.getTokenPayload();
    if (!payload) return null;

    const id = String(
      payload['sub'] ??
      payload['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier'] ??
      ''
    ).trim();
    const nome = String(payload['name'] ?? '').trim();
    const email = String(
      payload['email'] ??
      payload['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'] ??
      ''
    ).trim().toLowerCase();
    const tenantId = String(
      payload['tenantId'] ??
      payload['tenant'] ??
      payload['TenantId'] ??
      ''
    ).trim();

    if (!id || !tenantId) return null;

    return {
      id,
      nome,
      email,
      tenantId,
      roles: this.extractRoles(payload),
    };
  }

  hasRole(role: string): boolean {
    const target = (role || '').toLowerCase();
    if (!target) return false;
    return this.getRoles().includes(target);
  }

  private cleanupExpiredRememberSession() {
    const token = localStorage.getItem(this.tokenStorageKey);
    if (!token) return;

    const expiresAtRaw = localStorage.getItem(this.tokenExpiresAtKey);
    if (!expiresAtRaw) {
      this.clearTokenStorage(localStorage);
      return;
    }

    const expiresAt = Number(expiresAtRaw);
    if (!Number.isFinite(expiresAt)) {
      this.clearTokenStorage(localStorage);
      return;
    }

    if (Date.now() > expiresAt) {
      this.clearTokenStorage(localStorage);
    }
  }

  private clearTokenStorage(storage: Storage) {
    storage.removeItem(this.tokenStorageKey);
    storage.removeItem(this.tokenExpiresAtKey);
  }

  private resolveLoginToken(response: LoginResponse | null | undefined): string | null {
    const raw = response?.token ?? response?.accessToken ?? null;
    const token = (raw ?? '').toString().trim();
    return token || null;
  }
}
