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;
  work_orders_closed: number;
}

interface WorkOrderMetricsCounts {
  open: number;
  inProgress: number;
  completed: number;
  completedCombined?: number;
  pendingReview: number;
  rejected: number;
  closed: number;
  cancelled?: number;
  total: number;
}

interface CompletionByTypeRate {
  type: string;
  total: number;
  open?: number;
  inProgress?: number;
  pendingReview?: number;
  completed?: number;
  closed?: number;
  completedCombined?: number;
  rejected?: number;
  cancelled?: number;
}

interface WorkOrderMetricsResponse {
  counts: WorkOrderMetricsCounts;
  work_order_chart: ChartResponse;
  type_status_chart?: ChartResponse;
  completion_by_type?: {
    labels: string[];
    rates: CompletionByTypeRate[];
  };
}

interface AssetDeviceStatusSummaryResponse {
  total_assets: number;
  assets_down: number;
  chart: ChartResponse & { total?: 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;
  };
}

interface LoginResponse {
  message: {
    full_name: string;
    user_id: string;
    sid: string;
  };
}

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;
  }

  // CSRF for guest requests (forgot password, post-reset logout)
  async getCSRFTokenForGuest(): Promise<string | null> {
    if (typeof window !== 'undefined') {
      const token = (window as any).csrf_token;
      if (token && token !== 'None') {
        return 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 {
      // guest CSRF is optional for some sites
    }
    return null;
  }

  async requestPasswordReset(user: string, signal?: AbortSignal): Promise<void> {
    const trimmed = user.trim();
    if (!trimmed) {
      throw new ApiError('User required', undefined, 'USER_REQUIRED');
    }

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

    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 45000);

    const onAbort = () => controller.abort();
    if (signal) {
      if (signal.aborted) {
        clearTimeout(timeoutId);
        throw new ApiError('Aborted', undefined, 'TIMEOUT');
      }
      signal.addEventListener('abort', onAbort);
    }

    try {
      const response = await fetch(`${this.baseURL}${this.endpoints.RESET_PASSWORD}`, {
        method: 'POST',
        headers,
        body: `user=${encodeURIComponent(trimmed)}`,
        credentials: 'include',
        signal: controller.signal,
      });

      const data: ApiResponse = await response.json().catch(() => ({}));
      const message =
        typeof data.message === 'string'
          ? data.message
          : typeof data.message === 'object' && data.message !== null
            ? String((data.message as { message?: string }).message ?? '')
            : '';

      if (response.status === 404 || message === 'not found' || /not found/i.test(message)) {
        throw new ApiError('not found', 404, 'NOT_FOUND');
      }
      if (response.status === 403) {
        throw new ApiError('forbidden', 403, 'FORBIDDEN');
      }
      if (message === 'disabled' || message === 'not allowed') {
        throw new ApiError(message, 200, 'CANNOT_RESET');
      }
      if (!response.ok) {
        throw new ApiError(data.error || `HTTP error! status: ${response.status}`, response.status);
      }
    } catch (error) {
      if (error instanceof ApiError) {
        throw error;
      }
      if (error instanceof DOMException && error.name === 'AbortError') {
        throw new ApiError('timeout', undefined, 'TIMEOUT');
      }
      if (error instanceof Error && error.name === 'AbortError') {
        throw new ApiError('timeout', undefined, 'TIMEOUT');
      }
      throw error;
    } finally {
      clearTimeout(timeoutId);
      if (signal) {
        signal.removeEventListener('abort', onAbort);
      }
    }
  }

  // Get CSRF Token for authenticated requests
  async getCSRFToken(): Promise<string | null> {
    try {
      if (typeof window !== 'undefined') {
        const windowToken = (window as any).csrf_token;
        if (windowToken && windowToken !== 'None') {
          return windowToken;
        }
      }

      const user = localStorage.getItem('user');
      if (!user) {
        return null;
      }

      const response = await fetch(`${this.baseURL}${this.endpoints.CSRF_TOKEN}`, {
        method: 'GET',
        headers: {
          Accept: 'application/json',
        },
        credentials: 'include',
      });

      if (!response.ok) {
        return null;
      }

      const data: ApiResponse<{ csrf_token?: string }> = await response.json();
      const token = data.message?.csrf_token || null;
      if (token && typeof window !== 'undefined') {
        (window as any).csrf_token = token;
      }
      return token;
    } catch {
      return null;
    }
  }

  async refreshCSRFToken(): Promise<string | null> {
    if (typeof window !== 'undefined') {
      (window as any).csrf_token = null;
    }
    return this.getCSRFToken();
  }

  // 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 mutating requests only
    const method = (defaultOptions.method || 'GET').toUpperCase();
    if (method !== 'GET' && method !== 'HEAD') {
      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;
    }
  }

  // Authentication Methods
  async login(credentials: LoginCredentials): Promise<LoginResponse> {
    // Only log in development mode (hide sensitive data in production)
    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 url = `${this.baseURL}${this.endpoints.LOGIN}`;
    
    try {
      const controller = new AbortController();
      const timeoutId = setTimeout(() => controller.abort(), this.timeout);
      
      const response = await fetch(url, {
        method: 'POST',
        headers: {
          'Accept': 'application/json'
        },
        body: formData,
        credentials: 'include', // Important: Include cookies
        signal: controller.signal
      });
      
      clearTimeout(timeoutId);
      
      if (!response.ok) {
        const errorData: ApiResponse = await response.json().catch(() => ({}));
        // Hide detailed error messages in production
        const errorMessage = import.meta.env.DEV 
          ? (errorData.error || `HTTP error! status: ${response.status}`)
          : 'Invalid credentials. Please try again.';
        throw new ApiError(errorMessage, response.status);
      }
      
      const data: any = await response.json();

      await this.refreshCSRFToken();
      
      // Handle Frappe API response format
      // Check if message is a string (like "Logged In")
      if (typeof data.message === 'string' && data.message === 'Logged In') {
        const userData = {
          full_name: data.full_name,
          user_id: data.user || data.email,
          home_page: data.home_page,
          sid: data.sid
        };
        
        if (import.meta.env.DEV) {
          console.log('[API Service] Login successful');
        }
        
        return { message: userData } as LoginResponse;
      }
      
      // If message contains user object
      if (data.message && typeof data.message === 'object') {
        if (import.meta.env.DEV) {
          console.log('[API Service] Login successful');
        }
        return { message: data.message } as LoginResponse;
      }
      
      // Sometimes Frappe returns full_name, user directly
      if (data.full_name || data.user) {
        if (import.meta.env.DEV) {
          console.log('[API Service] Login successful');
        }
        return { message: data } as LoginResponse;
      }
      
      return { message: data } as LoginResponse;
      
    } catch (error) {
      if (error instanceof Error) {
        // Only log detailed errors in development
        if (import.meta.env.DEV) {
          console.error('[API Service] Login error:', error.message);
        }
        throw new ApiError(
          import.meta.env.DEV ? error.message : 'Login failed. Please try again.'
        );
      }
      throw error;
    }
  }

  // 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(filters?: Record<string, string>): Promise<NumberCards> {
    const params = new URLSearchParams();
    if (filters?.company) params.append('company', filters.company);
    if (filters?.site_name) params.append('site_name', filters.site_name);
    const qs = params.toString();
    const url = qs
      ? `${this.endpoints.DASHBOARD_NUMBER_CARDS}?${qs}`
      : this.endpoints.DASHBOARD_NUMBER_CARDS;
    return this.apiCall<NumberCards>(url);
  }

  async getDashboardWorkOrderMetrics(filters?: Record<string, string>): Promise<WorkOrderMetricsResponse> {
    const params = new URLSearchParams();
    if (filters?.company) params.append('company', filters.company);
    if (filters?.site_name) params.append('site_name', filters.site_name);
    if (filters?.work_order_type) params.append('work_order_type', filters.work_order_type);
    if (filters?.from_date) params.append('from_date', filters.from_date);
    if (filters?.to_date) params.append('to_date', filters.to_date);
    const qs = params.toString();
    const url = qs
      ? `${this.endpoints.DASHBOARD_WORK_ORDER_METRICS}?${qs}`
      : this.endpoints.DASHBOARD_WORK_ORDER_METRICS;
    return this.apiCall<WorkOrderMetricsResponse>(url);
  }

  async getAssetDeviceStatusSummary(filters?: Record<string, string>): Promise<AssetDeviceStatusSummaryResponse> {
    const params = new URLSearchParams();
    if (filters?.company) params.append('company', filters.company);
    if (filters?.site_name) params.append('site_name', filters.site_name);
    if (filters?.from_date) params.append('from_date', filters.from_date);
    if (filters?.to_date) params.append('to_date', filters.to_date);
    const qs = params.toString();
    const url = qs
      ? `${this.endpoints.DASHBOARD_ASSET_DEVICE_STATUS}?${qs}`
      : this.endpoints.DASHBOARD_ASSET_DEVICE_STATUS;
    return this.apiCall<AssetDeviceStatusSummaryResponse>(url);
  }

  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 };
