import API_CONFIG from '../config/api';

// Define interfaces locally to avoid import issues
interface ApiResponse<T = any> {
  message?: T;
  error?: string;
  status_code?: number;
}

interface UserDetails {
  user_id: string;
  full_name: string;
  email: string;
  user_image?: string;
  roles: string[];
  permissions: Record<string, {
    read: boolean;
    write: boolean;
    create: boolean;
    delete: boolean;
  }>;
  last_login?: string;
  enabled: boolean;
  creation: string;
  modified: string;
  language: string;
  custom_site_name?: string;
  custom_phcc_site_name?: string;
}

interface DocTypeRecord {
  name: string;
  creation: string;
  modified: string;
  modified_by: string;
  owner: string;
  docstatus: number;
  [key: string]: any;
}

interface DocTypeRecordsResponse {
  records: DocTypeRecord[];
  total_count: number;
  limit: number;
  offset: number;
  has_more: boolean;
  doctype: string;
}

interface DashboardStats {
  total_users: number;
  total_customers: number;
  total_items: number;
  total_orders: number;
  recent_activities: RecentActivity[];
}

// Dashboard number cards
interface NumberCards {
  total_assets: number;
  work_orders_open: number;
  work_orders_in_progress: number;
  work_orders_completed: number;
}

// Dashboard chart payload
interface ChartDataset { name: string; values: number[]; color?: string }
interface ChartResponse {
  labels: string[];
  datasets: ChartDataset[];
  type: 'Bar' | 'Pie' | 'Line' | string;
  options?: Record<string, any>;
}

interface RecentActivity {
  type: string;
  name: string;
  title: string;
  creation: string;
}

interface KycRecord {
  name: string;
  kyc_status: string;
  kyc_type: string;
  creation: string;
  modified: string;
}

interface KycDetailsResponse {
  records: KycRecord[];
  summary: {
    total: number;
    pending: number;
    approved: number;
  };
}

export interface LoginUserMessage {
  full_name?: string;
  user_id?: string;
  email?: string;
  home_page?: string;
  sid?: string;
}

export interface TwoFactorVerification {
  method: string;
  setup?: boolean;
  prompt?: string;
  token_delivery?: boolean;
}

export type LoginResult =
  | { status: 'logged_in'; user: LoginUserMessage }
  | {
      status: 'two_factor_required';
      tmp_id: string;
      verification: TwoFactorVerification;
    };

interface LoginCredentials {
  email: string;
  password: string;
}

interface FileUploadOptions {
  file: File;
  doctype: string;
  docname: string;
  fieldname: string;
}

interface RequestOptions {
  method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
  headers?: Record<string, string>;
  body?: any;
}

// USER PERMISSION INTERFACES
interface RestrictionInfo {
  field: string;
  values: string[];
  count: number;
}

interface PermissionFiltersResponse {
  is_admin: boolean;
  filters: Record<string, any>;
  restrictions: Record<string, RestrictionInfo>;
  target_doctype: string;
  user?: string;
  total_restrictions?: number;
  warning?: string;
}

interface AllowedValuesResponse {
  is_admin: boolean;
  allowed_values: string[];
  default_value?: string | null;
  has_restriction: boolean;
  allow_doctype: string;
}

interface DocumentAccessResponse {
  has_access: boolean;
  is_admin?: boolean;
  no_restrictions?: boolean;
  error?: string;
  denied_by?: string;
  field?: string;
  document_value?: string;
  allowed_values?: string[];
}

interface UserDefaultsResponse {
  is_admin: boolean;
  defaults: Record<string, string>;
}

class ApiService {
  private baseURL: string;
  // private token: string | null = null;
  private endpoints: Record<string, string>;
  private defaultHeaders: Record<string, string>;
  private timeout: number;

  constructor() {
    this.baseURL = API_CONFIG.BASE_URL;
    this.endpoints = API_CONFIG.ENDPOINTS;
    this.defaultHeaders = API_CONFIG.DEFAULT_HEADERS;
    this.timeout = API_CONFIG.TIMEOUT;
  }

  // Get CSRF Token for authenticated requests
  async getCSRFToken(): Promise<string | null> {
    try {
      // First, try to get CSRF token from window (injected by Frappe in HTML)
      if (typeof window !== 'undefined' && (window as any).csrf_token) {
        return (window as any).csrf_token;
      }
      
      // If not in window, try to fetch from API (but only if user is authenticated)
      // Check if user is logged in by checking localStorage
      const user = localStorage.getItem('user');
      if (!user) {
        // User not logged in, skip CSRF token fetch
        return null;
      }
      
      const response = await fetch(`${this.baseURL}${this.endpoints.CSRF_TOKEN}`, {
        method: 'GET',
        headers: {
          'Accept': 'application/json'
        },
        credentials: 'include' // Include cookies for session
      });
      
      if (response.ok) {
        const data: ApiResponse<string> = await response.json();
        return data.message || null;
      } else {
        // Silently fail - CSRF token not required for GET requests
        return null;
      }
    } catch (error) {
      // Silently fail - CSRF token not required for GET requests
      return null;
    }
  }

  // Generic API call method
  async apiCall<T = any>(endpoint: string, options: RequestOptions = {}): Promise<T> {
    const url = `${this.baseURL}${endpoint}`;
    
    const defaultOptions: RequestInit = {
      method: 'GET',
      headers: {
        ...this.defaultHeaders,
        ...options.headers,
        // 'Authorization': `token ${this.token}`
      },
      ...options
    };

    // Add CSRF token for non-GET requests
    // if (defaultOptions.method !== 'GET') {
      const csrfToken = await this.getCSRFToken();
      if (csrfToken) {
        (defaultOptions.headers as Record<string, string>)['X-Frappe-CSRF-Token'] = csrfToken;
      }
    // }

    try {
      const controller = new AbortController();
      const timeoutId = setTimeout(() => controller.abort(), this.timeout);

      const response = await fetch(url, {
        ...defaultOptions,
        signal: controller.signal
      });

      clearTimeout(timeoutId);
      
      if (!response.ok) {
        const errorData: ApiResponse = await response.json().catch(() => ({}));
        throw new ApiError(
          errorData.error || `HTTP error! status: ${response.status}`,
          response.status
        );
      }
      
      const data: ApiResponse<T> = await response.json();
      
      // Handle Frappe API response format
      if (data.message !== undefined) {
        return data.message;
      }
      
      return data as T;
    } catch (error) {
      if (error instanceof Error) {
        console.error('API call failed:', error);
        throw new ApiError(error.message);
      }
      throw error;
    }
  }

  private parseFrappeLoginError(data: Record<string, unknown>, status: number): string {
    if (typeof data.message === 'string' && data.message) return data.message;
    if (typeof data.exc === 'string' && data.exc) {
      const match = data.exc.match(/:\s*(.+)$/);
      return match ? match[1].trim() : data.exc;
    }
    if (data._server_messages) {
      try {
        const msgs = JSON.parse(String(data._server_messages)) as string[];
        const parsed = msgs.map((m) => JSON.parse(m) as { message?: string });
        const text = parsed.map((p) => p.message).filter(Boolean).join(' ');
        if (text) return text;
      } catch {
        /* ignore */
      }
    }
    if (status === 401) return 'Invalid credentials or verification code.';
    return 'Login failed. Please try again.';
  }

  private parseLoggedInUser(data: Record<string, unknown>, fallbackEmail?: string): LoginUserMessage | null {
    if (typeof data.message === 'string' && data.message === 'Logged In') {
      return {
        full_name: data.full_name as string | undefined,
        user_id: (data.user as string) || fallbackEmail,
        home_page: data.home_page as string | undefined,
        sid: data.sid as string | undefined,
        email: fallbackEmail,
      };
    }
    if (data.message && typeof data.message === 'object') {
      return data.message as LoginUserMessage;
    }
    if (data.full_name || data.user) {
      return {
        full_name: data.full_name as string | undefined,
        user_id: (data.user as string) || fallbackEmail,
        home_page: data.home_page as string | undefined,
        sid: data.sid as string | undefined,
        email: fallbackEmail,
      };
    }
    return null;
  }

  private async postLoginRequest(formData: FormData): Promise<Record<string, unknown>> {
    const url = `${this.baseURL}${this.endpoints.LOGIN}`;
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), this.timeout);

    try {
      const response = await fetch(url, {
        method: 'POST',
        headers: { Accept: 'application/json' },
        body: formData,
        credentials: 'include',
        signal: controller.signal,
      });

      const raw = await response.text();
      let data: Record<string, unknown> = {};
      try {
        data = raw ? JSON.parse(raw) : {};
      } catch {
        data = {};
      }

      if (!response.ok) {
        const errorMessage = import.meta.env.DEV
          ? this.parseFrappeLoginError(data, response.status)
          : response.status === 401
            ? 'Invalid credentials or verification code.'
            : 'Login failed. Please try again.';
        throw new ApiError(errorMessage, response.status);
      }

      return data;
    } finally {
      clearTimeout(timeoutId);
    }
  }

  private mapLoginResponse(
    data: Record<string, unknown>,
    fallbackEmail?: string
  ): LoginResult {
    const verification = data.verification as TwoFactorVerification | undefined;
    const tmpId = data.tmp_id as string | undefined;
    const message = data.message;

    if (
      verification &&
      tmpId &&
      message !== 'Logged In'
    ) {
      return {
        status: 'two_factor_required',
        tmp_id: tmpId,
        verification,
      };
    }

    const user = this.parseLoggedInUser(data, fallbackEmail);
    if (user && (message === 'Logged In' || user.sid || user.user_id)) {
      return { status: 'logged_in', user };
    }

    throw new ApiError('Unexpected login response.', 500, 'INVALID_RESPONSE');
  }

  // Authentication Methods
  async login(credentials: LoginCredentials): Promise<LoginResult> {
    if (import.meta.env.DEV) {
      console.log('[API Service] Login attempt for:', credentials.email);
    }

    const formData = new FormData();
    formData.append('usr', credentials.email);
    formData.append('pwd', credentials.password);

    const data = await this.postLoginRequest(formData);
    const result = this.mapLoginResponse(data, credentials.email);

    if (import.meta.env.DEV && result.status === 'logged_in') {
      console.log('[API Service] Login successful');
    }
    return result;
  }

  /** Second step after password when Frappe requires OTP (OTP App). */
  async verifyLoginOtp(tmpId: string, otp: string): Promise<LoginResult> {
    const formData = new FormData();
    formData.append('otp', otp.trim());
    formData.append('tmp_id', tmpId);

    const data = await this.postLoginRequest(formData);
    const result = this.mapLoginResponse(data);

    if (result.status !== 'logged_in') {
      throw new ApiError('Verification failed. Please try again.', 401, 'OTP_FAILED');
    }
    return result;
  }

  /**
   * Guest POST to Frappe — sends the same password-reset email as Desk "Forgot password".
   * The email link targets the Frappe site URL (update password on the server).
   */
  async requestPasswordReset(userEmail: string, signal?: AbortSignal): Promise<void> {
    const trimmed = userEmail.trim();
    if (!trimmed) {
      throw new ApiError('Email is required', 400, 'EMPTY_EMAIL');
    }

    const csrfToken = await this.getCSRFTokenForGuest();
    const headers: Record<string, string> = {
      Accept: 'application/json',
      'Content-Type': 'application/x-www-form-urlencoded',
    };
    if (csrfToken) {
      headers['X-Frappe-CSRF-Token'] = csrfToken;
    }

    const url = `${this.baseURL}${this.endpoints.RESET_PASSWORD}`;
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 45000);
    const combinedSignal = signal
      ? AbortSignal.any([signal, controller.signal])
      : controller.signal;

    try {
      const response = await fetch(url, {
        method: 'POST',
        headers,
        body: new URLSearchParams({ user: trimmed }).toString(),
        credentials: 'include',
        signal: combinedSignal,
      });

      const raw = await response.text();
      let data: { message?: unknown } = {};
      try {
        data = raw ? JSON.parse(raw) : {};
      } catch {
        data = {};
      }

      const msg =
        typeof data.message === 'string'
          ? data.message
          : typeof data.message === 'object' &&
              data.message !== null &&
              'message' in (data.message as object)
            ? String((data.message as { message?: string }).message)
            : '';

      if (response.status === 404 || msg.toLowerCase().includes('not found')) {
        throw new ApiError('User not found', 404, 'USER_NOT_FOUND');
      }

      if (response.status === 403) {
        throw new ApiError('Could not send reset email', 403, 'FORBIDDEN');
      }

      if (!response.ok) {
        throw new ApiError(
          msg || `HTTP error! status: ${response.status}`,
          response.status,
          'REQUEST_FAILED'
        );
      }

      if (msg === 'disabled' || msg === 'not allowed') {
        throw new ApiError('Reset not allowed', 200, 'RESET_NOT_ALLOWED');
      }
    } finally {
      clearTimeout(timeoutId);
    }
  }

  /** CSRF for guest flows (login page injects window.csrf_token). */
  async getCSRFTokenForGuest(): Promise<string | null> {
    const win = typeof window !== 'undefined' ? (window as { csrf_token?: string }) : undefined;
    if (win?.csrf_token) {
      return win.csrf_token;
    }
    try {
      const response = await fetch(`${this.baseURL}${this.endpoints.CSRF_TOKEN}`, {
        method: 'GET',
        headers: { Accept: 'application/json' },
        credentials: 'include',
      });
      if (response.ok) {
        const data: ApiResponse<string> = await response.json();
        return data.message || null;
      }
    } catch {
      /* ignore */
    }
    return null;
  }

  // async login(credentials: LoginCredentials): Promise<LoginResponse> {
  //   const formData = new FormData();
  //   formData.append('usr', credentials.email);
  //   formData.append('pwd', credentials.password);

  //   const response = await fetch(`${this.baseURL}/api/method/login`, {
  //     method: 'POST',
  //     body: formData,
  //   });

  //   const data = await response.json();

  //   if (data.message === 'Logged In') {
  //     // Now get API keys or generate token
  //     await this.fetchApiKeys(credentials);
  //     return { message: data } as LoginResponse;
  //   }

  //   throw new ApiError('Login failed');
  // }

  // private async fetchApiKeys(credentials: LoginCredentials): Promise<void> {
  //   // Use Basic Auth to get API keys
  //   const basicAuth = btoa(`${credentials.email}:${credentials.password}`);

  //   // First, check if user has API keys, if not generate them
  //   const response = await fetch(
  //     `${this.baseURL}/api/method/frappe.core.doctype.user.user.generate_keys?user=${encodeURIComponent(credentials.email)}`,
  //     {
  //       method: 'POST',
  //       headers: {
  //         'Authorization': `Basic ${basicAuth}`,
  //         'Accept': 'application/json',
  //       },
  //     }
  //   );

  //   const data = await response.json();

  //   if (data.message?.api_secret) {
  //     // Fetch the api_key from user doc
  //     const userResponse = await fetch(
  //       `${this.baseURL}/api/resource/User/${encodeURIComponent(credentials.email)}`,
  //       {
  //         method: 'GET',
  //         headers: {
  //           'Authorization': `Basic ${basicAuth}`,
  //           'Accept': 'application/json',
  //         },
  //       }
  //     );

  //     const userData = await userResponse.json();
  //     const apiKey = userData.data.api_key;
  //     const apiSecret = data.message.api_secret;

  //     // Store the token
  //     this.token = `${apiKey}:${apiSecret}`;
  //     localStorage.setItem('auth_token', this.token);
  //     localStorage.setItem('user_email', credentials.email);
  //   }
  // }

  async logout(): Promise<void> {
    await this.apiCall(this.endpoints.LOGOUT, {
      method: 'POST'
    });
  }

  // User Management
  async getUserDetails(userId?: string): Promise<UserDetails> {
    const params = userId ? `?user_id=${userId}` : '';
    return this.apiCall<UserDetails>(`${this.endpoints.USER_DETAILS}${params}`);
  }

  // Data Management
  async getDoctypeRecords(
    doctype: string, 
    filters?: Record<string, any>, 
    fields?: string[], 
    limit: number = 20, 
    offset: number = 0
  ): Promise<DocTypeRecordsResponse> {
    const params = new URLSearchParams({
      doctype,
      limit: limit.toString(),
      offset: offset.toString()
    });
    
    if (filters) {
      params.append('filters', JSON.stringify(filters));
    }
    
    if (fields) {
      params.append('fields', JSON.stringify(fields));
    }
    
    return this.apiCall<DocTypeRecordsResponse>(`${this.endpoints.DOCTYPE_RECORDS}?${params}`);
  }

  // Dashboard
  async getDashboardStats(): Promise<DashboardStats> {
    return this.apiCall<DashboardStats>(this.endpoints.DASHBOARD_STATS);
  }

  async getNumberCards(): Promise<NumberCards> {
    return this.apiCall<NumberCards>(this.endpoints.DASHBOARD_NUMBER_CARDS);
  }

  async listDashboardCharts(publicOnly: boolean = true): Promise<{ charts: any[] }> {
    const params = new URLSearchParams({ public_only: publicOnly ? '1' : '0' });
    return this.apiCall<{ charts: any[] }>(`${this.endpoints.DASHBOARD_LIST_CHARTS}?${params}`);
  }

  async getDashboardChartData(chartName: string, filters?: Record<string, any>): Promise<ChartResponse> {
    const params = new URLSearchParams({ chart_name: chartName });
    if (filters) params.append('report_filters', JSON.stringify(filters));
    return this.apiCall<ChartResponse>(`${this.endpoints.DASHBOARD_CHART_DATA}?${params}`);
  }

  // KYC Management
  async getKycDetails(): Promise<KycDetailsResponse> {
    return this.apiCall<KycDetailsResponse>(this.endpoints.KYC_DETAILS);
  }

  // File Upload
  async uploadFile(options: FileUploadOptions): Promise<any> {
    const formData = new FormData();
    formData.append('file', options.file);
    formData.append('doctype', options.doctype);
    formData.append('docname', options.docname);
    formData.append('fieldname', options.fieldname);
    
    return this.apiCall(this.endpoints.UPLOAD_FILE, {
      method: 'POST',
      headers: {}, // Don't set Content-Type for FormData
      body: formData
    });
  }

  // USER PERMISSION METHODS
  async getUserPermissions(userId?: string): Promise<any> {
    const params = userId ? `?user=${encodeURIComponent(userId)}` : '';
    return this.apiCall(`${this.endpoints.GET_USER_PERMISSIONS}${params}`);
  }

  async getPermissionFilters(targetDoctype: string, userId?: string): Promise<PermissionFiltersResponse> {
    const params = new URLSearchParams({ target_doctype: targetDoctype });
    if (userId) params.append('user', userId);
    return this.apiCall<PermissionFiltersResponse>(`${this.endpoints.GET_PERMISSION_FILTERS}?${params}`);
  }

  async getAllowedValues(allowDoctype: string, userId?: string): Promise<AllowedValuesResponse> {
    const params = new URLSearchParams({ allow_doctype: allowDoctype });
    if (userId) params.append('user', userId);
    return this.apiCall<AllowedValuesResponse>(`${this.endpoints.GET_ALLOWED_VALUES}?${params}`);
  }

  async checkDocumentAccess(doctype: string, docname: string, userId?: string): Promise<DocumentAccessResponse> {
    const params = new URLSearchParams({ doctype, docname });
    if (userId) params.append('user', userId);
    return this.apiCall<DocumentAccessResponse>(`${this.endpoints.CHECK_DOCUMENT_ACCESS}?${params}`);
  }

  async getConfiguredDoctypes(): Promise<any> {
    return this.apiCall(this.endpoints.GET_CONFIGURED_DOCTYPES);
  }

  async getUserDefaults(userId?: string): Promise<UserDefaultsResponse> {
    const params = userId ? `?user=${encodeURIComponent(userId)}` : '';
    return this.apiCall<UserDefaultsResponse>(`${this.endpoints.GET_USER_DEFAULTS}${params}`);
  }

  // Utility Methods
  isAuthenticated(): boolean {
    // Check if user is authenticated (implement based on your auth strategy)
    return !!localStorage.getItem('frappe_session_id');
  }

  getSessionId(): string | null {
    return localStorage.getItem('frappe_session_id');
  }

  setSessionId(sessionId: string): void {
    localStorage.setItem('frappe_session_id', sessionId);
  }
}

// Custom Error Class
class ApiError extends Error {
  public status?: number;
  public code?: string;

  constructor(message: string, status?: number, code?: string) {
    super(message);
    this.name = 'ApiError';
    this.status = status;
    this.code = code;
  }
}

// Create and export singleton instance
const apiService = new ApiService();
export default apiService;
export { ApiError };
export type { LoginCredentials };
