import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable, of, forkJoin } from 'rxjs';
import { map, catchError, switchMap } from 'rxjs/operators';
import { AgentLimit } from '../interfaces/agent-limit';
import { environment } from 'src/environments/environment.development';
import { normalizePage } from '@shared/paging/normalize-page';
import { ListParams, PagedResult } from '@shared/paging/paging';
import { AgentService } from './agent';

const USE_SERVER = true;
const API_BASE = '/api/v1/agent-limits';

// Interface to match the API response structure
interface AgentLimitApiResponse {
  id: number;
  code: string;
  configCode: string;
  nom: string;
  isDefault: boolean;
  actif: boolean;
  betMin?: number;
  betMax?: number;
  maxBet?: number;
  maxDisburseBet?: number;
  airtimeMin?: number;
  airtimeMax?: number;
  createdAt?: string;
  createdBy?: string;
  default?: boolean;
}

@Injectable({ providedIn: 'root' })
export class AgentLimitService {
  private apiUrl = environment.apiBaseUrl + API_BASE;

  constructor(private http: HttpClient, private agentService: AgentService) {}

  // Helper method to get ngrok bypass headers
  private getNgrokHeaders(): Record<string, string> {
    const isNgrok =
      environment.apiBaseUrl.includes('ngrok-free.app') ||
      environment.apiBaseUrl.includes('ngrok.io') ||
      environment.apiBaseUrl.includes('ngrok');
    return isNgrok ? { 'ngrok-skip-browser-warning': 'true' } : {};
  }

  // Transform API response to AgentLimit
  private transformLimit(apiLimit: AgentLimitApiResponse): AgentLimit {
    return {
      id: String(apiLimit.id),
      code: apiLimit.code,
      configCode: apiLimit.configCode,
      nom: apiLimit.nom,
      isDefault: apiLimit.isDefault ?? apiLimit.default ?? false,
      actif: apiLimit.actif,
      betMin: apiLimit.betMin,
      betMax: apiLimit.betMax,
      maxBet: apiLimit.maxBet,
      maxDisburseBet: apiLimit.maxDisburseBet,
      airtimeMin: apiLimit.airtimeMin,
      airtimeMax: apiLimit.airtimeMax,
      createdAt: apiLimit.createdAt,
      createdBy: apiLimit.createdBy,
    };
  }

  // Transform AgentLimit to API payload
  private transformToApiPayload(limit: Partial<AgentLimit>): any {
    const payload: any = {};
    if (limit.code !== undefined) payload.code = limit.code;
    if (limit.configCode !== undefined) payload.configCode = limit.configCode;
    if (limit.nom !== undefined) payload.nom = limit.nom;
    if (limit.isDefault !== undefined) {
      payload.isDefault = limit.isDefault;
      payload.default = limit.isDefault;
    }
    if (limit.actif !== undefined) payload.actif = limit.actif;
    if (limit.betMin !== undefined) payload.betMin = limit.betMin;
    if (limit.betMax !== undefined) payload.betMax = limit.betMax;
    if (limit.maxBet !== undefined) payload.maxBet = limit.maxBet;
    if (limit.maxDisburseBet !== undefined) payload.maxDisburseBet = limit.maxDisburseBet;
    if (limit.airtimeMin !== undefined) payload.airtimeMin = limit.airtimeMin;
    if (limit.airtimeMax !== undefined) payload.airtimeMax = limit.airtimeMax;
    if (limit.createdBy !== undefined) payload.createdBy = limit.createdBy;
    return payload;
  }

  // GET /api/v1/agent-limits/{id} - Get by ID
  getById(id: string): Observable<AgentLimit | undefined> {
    if (USE_SERVER) {
      return this.http
        .get<AgentLimitApiResponse>(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() })
        .pipe(
          map((apiLimit) => this.transformLimit(apiLimit)),
          catchError((err) => {
            console.error(`Error fetching agent limit ${id}:`, err);
            return of(undefined);
          })
        );
    }
    return of(undefined);
  }

  // GET /api/v1/agent-limits - List all
  list(params?: ListParams): Observable<PagedResult<AgentLimit>> {
    if (USE_SERVER) {
      let httpParams = new HttpParams();
      if (params) {
        if (params.page) httpParams = httpParams.set('page', params.page.toString());
        if (params.size) httpParams = httpParams.set('size', params.size.toString());
        if (params.search) httpParams = httpParams.set('search', params.search);
        if (params.sortKey) httpParams = httpParams.set('sortKey', params.sortKey);
        if (params.sortDir) httpParams = httpParams.set('sortDir', params.sortDir);
      }

      return this.http
        .get<AgentLimitApiResponse[]>(this.apiUrl, {
          params: httpParams,
          headers: this.getNgrokHeaders(),
        })
        .pipe(
          map((list) => {
            const limits = list.map((apiLimit) => this.transformLimit(apiLimit));
            // If pagination params provided, return paginated result
            if (params) {
              return normalizePage<AgentLimit>(
                { data: limits, meta: { total: limits.length } },
                params.page || 1,
                params.size || 10
              );
            }
            // Otherwise return all as single page
            return normalizePage<AgentLimit>(
              { data: limits, meta: { total: limits.length } },
              1,
              limits.length
            );
          }),
          catchError((err) => {
            console.error('Error fetching agent limits:', err);
            return of(normalizePage<AgentLimit>({ data: [], meta: { total: 0 } }, 1, 10));
          })
        );
    }
    return of(normalizePage<AgentLimit>({ data: [], meta: { total: 0 } }, 1, 10));
  }

  // POST /api/v1/agent-limits - Create
  create(payload: Omit<AgentLimit, 'id' | 'createdAt'>): Observable<AgentLimit> {
    if (USE_SERVER) {
      const apiPayload = this.transformToApiPayload(payload);
      return this.http
        .post<AgentLimitApiResponse>(this.apiUrl, apiPayload, { headers: this.getNgrokHeaders() })
        .pipe(
          switchMap((apiLimit) => {
            const limit = this.transformLimit(apiLimit);
            // If this limit is set as default, handle default assignment
            if (limit.isDefault) {
              return this.handleDefaultLimitChange(limit.id).pipe(map(() => limit));
            }
            return of(limit);
          }),
          catchError((err) => {
            console.error('Error creating agent limit:', err);
            throw err;
          })
        );
    }
    throw new Error('Server mode is required');
  }

  // PUT /api/v1/agent-limits/{id} - Update
  update(id: string, payload: Partial<AgentLimit>): Observable<AgentLimit | undefined> {
    if (USE_SERVER) {
      // Check if isDefault is being changed to true
      const isSettingDefault = payload.isDefault === true;
      const wasDefault = payload.isDefault !== undefined;

      return this.http
        .put<AgentLimitApiResponse>(`${this.apiUrl}/${id}`, this.transformToApiPayload(payload), {
          headers: this.getNgrokHeaders(),
        })
        .pipe(
          switchMap((apiLimit) => {
            const limit = this.transformLimit(apiLimit);
            // If this limit is being set as default, handle default assignment
            if (isSettingDefault) {
              return this.handleDefaultLimitChange(limit.id).pipe(map(() => limit));
            }
            return of(limit);
          }),
          catchError((err) => {
            console.error(`Error updating agent limit ${id}:`, err);
            return of(undefined);
          })
        );
    }
    return of(undefined);
  }

  // Helper method to handle default limit changes
  // When a limit is set as default:
  // 1. Find the previous default limit and unset it (preserving all other fields)
  // 2. Assign the new default limit to all agents
  private handleDefaultLimitChange(newDefaultLimitId: string): Observable<boolean> {
    // First, find the previous default limit
    return this.list({
      page: 1,
      size: 1000,
      search: '',
      sortKey: 'code',
      sortDir: 'asc',
    } as any).pipe(
      switchMap((result) => {
        const limits = result.content;
        const previousDefault = limits.find((l) => l.isDefault && l.id !== newDefaultLimitId);

        const operations: Observable<any>[] = [];

        // If there's a previous default, unset it while preserving all other fields
        if (previousDefault) {
          // Create a payload with all fields from previousDefault, but with isDefault set to false
          // This ensures we preserve all existing data
          const updatePayload: Partial<AgentLimit> = {
            code: previousDefault.code,
            configCode: previousDefault.configCode,
            nom: previousDefault.nom,
            isDefault: false,
            actif: previousDefault.actif,
            betMin: previousDefault.betMin,
            betMax: previousDefault.betMax,
            maxBet: previousDefault.maxBet,
            maxDisburseBet: previousDefault.maxDisburseBet,
            airtimeMin: previousDefault.airtimeMin,
            airtimeMax: previousDefault.airtimeMax,
          };

          // Use the update method with the full payload
          operations.push(
            this.update(previousDefault.id, updatePayload).pipe(
              map(() => true),
              catchError((err) => {
                console.error(`Error unsetting previous default limit ${previousDefault.id}:`, err);
                return of(null);
              })
            )
          );
        }

        // Assign the new default limit to all agents
        operations.push(this.agentService.updateAllAgentsLimitId(newDefaultLimitId));

        return forkJoin(operations).pipe(
          map(() => true),
          catchError((err) => {
            console.error('Error handling default limit change:', err);
            return of(false);
          })
        );
      }),
      catchError((err) => {
        console.error('Error fetching limits for default change:', err);
        return of(false);
      })
    );
  }

  // DELETE /api/v1/agent-limits/{id} - Delete
  delete(id: string): Observable<boolean> {
    if (USE_SERVER) {
      return this.http
        .delete<void>(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() })
        .pipe(
          map(() => true),
          catchError((err) => {
            console.error(`Error deleting agent limit ${id}:`, err);
            return of(false);
          })
        );
    }
    return of(false);
  }

  // GET /api/v1/agent-limits/search/{nom} - Search by nom
  search(query: string): Observable<AgentLimit[]> {
    if (USE_SERVER) {
      const searchTerm = encodeURIComponent(query.trim());
      return this.http
        .get<AgentLimitApiResponse[]>(`${this.apiUrl}/search/${searchTerm}`, {
          headers: this.getNgrokHeaders(),
        })
        .pipe(
          map((list) => list.map((apiLimit) => this.transformLimit(apiLimit))),
          catchError((err) => {
            console.error(`Error searching agent limits with query ${query}:`, err);
            return of([]);
          })
        );
    }
    return of([]);
  }

  // GET /api/v1/agent-limits/actif/{actif} - List by actif status
  getByActif(actif: boolean): Observable<AgentLimit[]> {
    if (USE_SERVER) {
      if (actif) {
        return this.http
          .get<AgentLimitApiResponse[]>(`${this.apiUrl}/actif`, {
            headers: this.getNgrokHeaders(),
          })
          .pipe(
            map((list) => list.map((apiLimit) => this.transformLimit(apiLimit))),
            catchError((err) => {
              console.error(`Error fetching agent limits by actif ${actif}:`, err);
              return of([]);
            })
          );
      } else {
        return this.http
          .get<AgentLimitApiResponse[]>(`${this.apiUrl}/inactif`, {
            headers: this.getNgrokHeaders(),
          })
          .pipe(
            map((list) => list.map((apiLimit) => this.transformLimit(apiLimit))),
            catchError((err) => {
              console.error(`Error fetching agent limits by actif ${actif}:`, err);
              return of([]);
            })
          );
      }
    }
    return of([]);
  }
}
