import {
  AutotaskConfig,
  AutotaskHeaders,
  QueryParams,
  ApiResponse,
  ApiError,
  Resource,
  Ticket,
  Task,
  Company,
  ConfigurationItem,
  Attachment,
  EntityField,
  PicklistValue,
  AutotaskTimeEntry,
} from '@/lib/types/autotask';

export class AutotaskClient {
  private config: AutotaskConfig;
  private rateLimiter: RateLimiter;

  constructor(config: AutotaskConfig) {
    this.config = config;
    this.rateLimiter = new RateLimiter(10); // 10 requests per second
  }

  private getAuthHeaders(impersonationResourceId?: number): Record<string, string> {
    // Using the direct header authentication method (not Basic auth)
    const headers: Record<string, string> = {
      'Username': this.config.username,
      'Secret': this.config.password,
      'APIIntegrationcode': this.config.apiIntegrationCode,
      'Content-Type': 'application/json',
      'Accept': 'application/json',
    };

    if (impersonationResourceId) {
      headers.ImpersonationResourceID = impersonationResourceId.toString();
    }

    return headers;
  }

  private async makeApiCall<T>(
    url: string,
    options: RequestInit
  ): Promise<T> {
    await this.rateLimiter.throttle();

    try {
      const response = await fetch(url, options);
      const responseText = await response.text();

      if (!response.ok) {
        console.error(`Autotask API error: ${response.status} - ${responseText}`);

        let errorMessage = `API Error: ${response.statusText}`;
        try {
          const errorData: ApiError = JSON.parse(responseText);
          if (errorData.errors && errorData.errors.length > 0) {
            errorMessage = errorData.errors.map((e) => e.message).join(', ');
          } else if (errorData.message) {
            errorMessage = errorData.message;
          }
        } catch (parseError) {
          errorMessage = responseText || errorMessage;
        }

        throw new Error(errorMessage);
      }

      try {
        return JSON.parse(responseText);
      } catch (parseError) {
        console.error('Failed to parse API response:', parseError);
        throw new Error('Invalid JSON response from Autotask API');
      }
    } catch (error) {
      console.error('API call failed:', error);
      throw error;
    }
  }

  private buildQueryString(params: QueryParams): string {
    if (!params.filter || params.filter.length === 0) {
      return '';
    }

    const query = { filter: params.filter };
    return `?search=${encodeURIComponent(JSON.stringify(query))}`;
  }

  async queryEntity<T>(
    entityName: string,
    params: QueryParams = {}
  ): Promise<T[]> {
    const queryString = this.buildQueryString(params);
    const url = `${this.config.apiUrl}/${entityName}/query${queryString}`;

    const response = await this.makeApiCall<ApiResponse<T>>(url, {
      method: 'GET',
      headers: this.getAuthHeaders(),
    });

    return response.items || [];
  }

  // Paginated query to handle large datasets
  async queryEntityPaginated<T>(
    entityName: string,
    params: QueryParams = {},
    pageSize: number = 500
  ): Promise<T[]> {
    const allItems: T[] = [];
    let page = 1;
    let nextPageUrl: string | null = null;

    while (true) {
      console.log(`Fetching ${entityName} page ${page} (max ${pageSize} records)...`);
      

      const requestBody: any = {
        MaxRecords: pageSize,
      };

      if (params.filter && params.filter.length > 0) {
        requestBody.filter = params.filter;
        console.log(`[${entityName}] Query filter:`, JSON.stringify(params.filter));
      }

      const url: string = nextPageUrl || `${this.config.apiUrl}/${entityName}/query`;
      console.log(`[${entityName}] Request URL: ${url}`);
      console.log(`[${entityName}] Request body:`, JSON.stringify(requestBody));

      const response: ApiResponse<T> = await this.makeApiCall<ApiResponse<T>>(url, {
        method: 'POST',
        headers: this.getAuthHeaders(),
        body: JSON.stringify(requestBody),
      });

      console.log(`[${entityName}] Response pageDetails:`, JSON.stringify(response.pageDetails));
      console.log(`[${entityName}] Response items count:`, response.items?.length || 0);

      const items = response.items || [];
      allItems.push(...items);
      
      console.log(`Fetched ${items.length} ${entityName}, total so far: ${allItems.length}`);

      // Check if there's a next page using Autotask's pageDetails
      if (response.pageDetails?.nextPageUrl) {
        nextPageUrl = response.pageDetails.nextPageUrl;
        page++;
      } else {
        // No more pages
        console.log(`No more pages for ${entityName}`);
        break;
      }
    }

    console.log(`Total ${entityName} fetched: ${allItems.length}`);
    return allItems;
  }

  async getEntityById<T>(entityName: string, id: number): Promise<T | null> {
    const url = `${this.config.apiUrl}/${entityName}/${id}`;

    const response = await this.makeApiCall<ApiResponse<T>>(url, {
      method: 'GET',
      headers: this.getAuthHeaders(),
    });

    return response.item || null;
  }

  async createEntity<T>(entityName: string, data: Partial<T>): Promise<T> {
    const url = `${this.config.apiUrl}/${entityName}`;

    const response = await this.makeApiCall<ApiResponse<T>>(url, {
      method: 'POST',
      headers: this.getAuthHeaders(),
      body: JSON.stringify(data),
    });

    if (!response.item) {
      throw new Error('Failed to create entity');
    }

    return response.item;
  }

  async updateEntity<T>(
    entityName: string,
    id: number,
    data: Partial<T>
  ): Promise<T> {
    const url = `${this.config.apiUrl}/${entityName}`;

    console.log(`Updating ${entityName} ${id} with data:`, data);

    const response = await this.makeApiCall<any>(url, {
      method: 'PUT',
      headers: this.getAuthHeaders(),
      body: JSON.stringify(data),
    });

    console.log(`Update response for ${entityName} ${id}:`, response);

    // Autotask returns { itemId: ... } on successful update, not { item: {...} }
    // We need to fetch the updated item
    if (response.itemId || response.item) {
      const itemId = response.itemId || (response.item as any)?.id || id;
      console.log(`Fetching updated ${entityName} ${itemId}`);
      
      // Fetch the updated item
      const updatedItem = await this.getEntityById<T>(entityName, itemId);
      if (updatedItem) {
        return updatedItem;
      }
    }

    console.error(`No item or itemId in response for ${entityName} ${id}:`, response);
    throw new Error('Failed to update entity - no item in response');
  }

  async deleteEntity(entityName: string, id: number): Promise<void> {
    const url = `${this.config.apiUrl}/${entityName}/${id}`;

    await this.makeApiCall<void>(url, {
      method: 'DELETE',
      headers: this.getAuthHeaders(),
    });
  }

  // Resource-specific methods
  async getResourceByEmail(email: string): Promise<Resource | null> {
    const resources = await this.queryEntity<Resource>('Resources', {
      filter: [{ op: 'eq', field: 'email', value: email }],
    });

    return resources.length > 0 ? resources[0] : null;
  }

  async getAllResources(): Promise<Resource[]> {
    return this.queryEntity<Resource>('Resources', {
      filter: [{ op: 'eq', field: 'isActive', value: true }],
    });
  }

  // Ticket-specific methods
  async getOpenTicketsByResource(resourceId: number): Promise<Ticket[]> {
    return this.queryEntity<Ticket>('Tickets', {
      filter: [
        { op: 'eq', field: 'assignedResourceID', value: resourceId },
        { op: 'noteq', field: 'status', value: 5 }, // Exclude completed
      ],
    });
  }

  async getTicketsByCompany(companyId: number): Promise<Ticket[]> {
    return this.queryEntity<Ticket>('Tickets', {
      filter: [{ op: 'eq', field: 'companyID', value: companyId }],
    });
  }

  async createTicket(ticket: Partial<Ticket>): Promise<Ticket> {
    return this.createEntity<Ticket>('Tickets', ticket);
  }

  async updateTicket(id: number, updates: Partial<Ticket>): Promise<Ticket> {
    return this.updateEntity<Ticket>('Tickets', id, updates);
  }

  // Task-specific methods
  async getTasksByResource(resourceId: number): Promise<Task[]> {
    return this.queryEntity<Task>('Tasks', {
      filter: [
        { op: 'eq', field: 'assignedResourceID', value: resourceId },
        { op: 'noteq', field: 'status', value: 5 }, // Exclude completed
      ],
    });
  }

  async getTasksByProject(projectId: number): Promise<Task[]> {
    return this.queryEntity<Task>('Tasks', {
      filter: [{ op: 'eq', field: 'projectID', value: projectId }],
    });
  }

  async createTask(task: Partial<Task>): Promise<Task> {
    return this.createEntity<Task>('Tasks', task);
  }

  async updateTask(id: number, updates: Partial<Task>): Promise<Task> {
    return this.updateEntity<Task>('Tasks', id, updates);
  }

  // Company-specific methods
  async getAllCompanies(): Promise<Company[]> {
    const companies = await this.queryEntity<Company>('Companies', {
      filter: [{ op: 'eq', field: 'isActive', value: true }],
    });

    return companies.sort((a, b) =>
      a.companyName.localeCompare(b.companyName)
    );
  }

  async getCompanyById(id: number): Promise<Company | null> {
    return this.getEntityById<Company>('Companies', id);
  }

  // Configuration Item methods
  async getConfigurationItemsByCompany(companyId: number): Promise<ConfigurationItem[]> {
    return this.queryEntity<ConfigurationItem>('ConfigurationItems', {
      filter: [
        { op: 'eq', field: 'companyID', value: companyId },
        { op: 'eq', field: 'isActive', value: true }
      ],
    });
  }

  async getAllConfigurationItems(): Promise<ConfigurationItem[]> {
    return this.queryEntity<ConfigurationItem>('ConfigurationItems', {
      filter: [{ op: 'eq', field: 'isActive', value: true }],
    });
  }

  async getConfigurationItemById(id: number): Promise<ConfigurationItem | null> {
    // Use query instead of direct GET as ConfigurationItems might not support direct ID access
    const items = await this.queryEntity<ConfigurationItem>('ConfigurationItems', {
      filter: [{ op: 'eq', field: 'id', value: id }],
    });
    
    return items.length > 0 ? items[0] : null;
  }

  async createConfigurationItem(item: Partial<ConfigurationItem>): Promise<ConfigurationItem> {
    return this.createEntity<ConfigurationItem>('ConfigurationItems', item);
  }

  async updateConfigurationItem(id: number, updates: Partial<ConfigurationItem>): Promise<ConfigurationItem> {
    return this.updateEntity<ConfigurationItem>('ConfigurationItems', id, updates);
  }

  // Picklist methods
  async getPicklistValues(
    entityName: string,
    fieldName: string
  ): Promise<Record<string | number, string>> {
    const url = `${this.config.apiUrl}/${entityName}/entityInformation/fields`;

    const response = await this.makeApiCall<{ fields: EntityField[] }>(url, {
      method: 'GET',
      headers: this.getAuthHeaders(),
    });

    const field = response.fields.find((f) => f.name === fieldName);

    if (field && field.picklistValues) {
      const picklistMap: Record<string | number, string> = {};
      field.picklistValues.forEach((item) => {
        picklistMap[item.value] = item.label;
      });
      return picklistMap;
    }

    return {};
  }

  async getTicketStatusPicklist(): Promise<Record<number, string>> {
    return this.getPicklistValues('Tickets', 'status');
  }

  async getTicketPriorityPicklist(): Promise<Record<number, string>> {
    return this.getPicklistValues('Tickets', 'priority');
  }

  async getTaskStatusPicklist(): Promise<Record<number, string>> {
    return this.getPicklistValues('Tasks', 'status');
  }

  // Attachment methods
  async uploadAttachment(
    entityName: string,
    entityId: number,
    fileBuffer: Buffer,
    fileName: string,
    impersonatorEmail?: string
  ): Promise<Attachment> {
    let impersonationResourceId: number | undefined;
    if (impersonatorEmail) {
      const resource = await this.getResourceByEmail(impersonatorEmail);
      if (resource) {
        impersonationResourceId = resource.id;
      }
    }

    const base64Data = fileBuffer.toString('base64');

    const payload: Partial<Attachment> = {
      id: 0,
      attachmentType: 'FILE_ATTACHMENT',
      fullPath: fileName,
      title: fileName,
      publish: 1, // All Autotask Users
      data: base64Data,
    };

    const url = `${this.config.apiUrl}/${entityName}/${entityId}/Attachments`;

    const response = await this.makeApiCall<ApiResponse<Attachment>>(url, {
      method: 'POST',
      headers: this.getAuthHeaders(impersonationResourceId),
      body: JSON.stringify(payload),
    });

    if (!response.item) {
      throw new Error('Failed to upload attachment');
    }

    return response.item;
  }

  async getAttachments(
    entityName: string,
    entityId: number
  ): Promise<Attachment[]> {
    const url = `${this.config.apiUrl}/${entityName}/${entityId}/Attachments`;

    const response = await this.makeApiCall<ApiResponse<Attachment>>(url, {
      method: 'GET',
      headers: this.getAuthHeaders(),
    });

    return response.items || [];
  }

  // Fetches a single attachment's full content (base64 `data` populated).
  // Confirmed live: the per-attachment-ID GET returns `{items:[...]}`
  // (list-shaped), NOT `{item:...}` like getEntityById/uploadAttachment —
  // do not "fix" this to read response.item, that would silently return
  // undefined content.
  async getAttachmentContent(
    entityName: string,
    entityId: number,
    attachmentId: number
  ): Promise<Attachment | null> {
    const url = `${this.config.apiUrl}/${entityName}/${entityId}/Attachments/${attachmentId}`;

    const response = await this.makeApiCall<ApiResponse<Attachment>>(url, {
      method: 'GET',
      headers: this.getAuthHeaders(),
    });

    return response.items?.[0] ?? null;
  }

  // Time Entries specific methods
  async getTimeEntriesByResource(resourceId: number): Promise<AutotaskTimeEntry[]> {
    return this.queryEntity<AutotaskTimeEntry>('TimeEntries', {
      filter: [{ op: 'eq', field: 'resourceID', value: resourceId }],
    });
  }

  async getTimeEntriesByTicket(ticketId: number): Promise<AutotaskTimeEntry[]> {
    return this.queryEntity<AutotaskTimeEntry>('TimeEntries', {
      filter: [{ op: 'eq', field: 'ticketID', value: ticketId }],
    });
  }

  async getTimeEntriesByTask(taskId: number): Promise<AutotaskTimeEntry[]> {
    return this.queryEntity<AutotaskTimeEntry>('TimeEntries', {
      filter: [{ op: 'eq', field: 'taskID', value: taskId }],
    });
  }

  async getTimeEntriesByProject(projectId: number): Promise<AutotaskTimeEntry[]> {
    return this.queryEntity<AutotaskTimeEntry>('TimeEntries', {
      filter: [{ op: 'eq', field: 'projectID', value: projectId }],
    });
  }

  async getTimeEntriesByCompany(companyId: number): Promise<AutotaskTimeEntry[]> {
    return this.queryEntity<AutotaskTimeEntry>('TimeEntries', {
      filter: [{ op: 'eq', field: 'companyID', value: companyId }],
    });
  }

  async getTimeEntriesByDateRange(startDate: Date, endDate: Date): Promise<AutotaskTimeEntry[]> {
    return this.queryEntity<AutotaskTimeEntry>('TimeEntries', {
      filter: [
        { op: 'gte', field: 'entryDate', value: startDate.toISOString() },
        { op: 'lte', field: 'entryDate', value: endDate.toISOString() },
      ],
    });
  }

  async createTimeEntry(timeEntry: Partial<AutotaskTimeEntry>): Promise<AutotaskTimeEntry> {
    return this.createEntity<AutotaskTimeEntry>('TimeEntries', timeEntry);
  }

  async updateTimeEntry(id: number, updates: Partial<AutotaskTimeEntry>): Promise<AutotaskTimeEntry> {
    return this.updateEntity<AutotaskTimeEntry>('TimeEntries', id, updates);
  }

  async deleteTimeEntry(id: number): Promise<void> {
    return this.deleteEntity('TimeEntries', id);
  }

  // Classification Icons methods
  async getClassificationIcons(): Promise<any[]> {
    // Autotask API endpoint for classification icons
    // Try ClassificationIcons endpoint
    const url = `${this.config.apiUrl}/ClassificationIcons`;
    
    const response = await this.makeApiCall<any>(url, {
      method: 'GET',
      headers: this.getAuthHeaders(),
    });

    return response.items || [];
  }

  async getFieldInfo(entityName: string): Promise<EntityField[]> {
    const url = `${this.config.apiUrl}/${entityName}/entityInformation/fields`;
    
    const response = await this.makeApiCall<any>(url, {
      method: 'GET',
      headers: this.getAuthHeaders(),
    });

    return response.fields || [];
  }
}

// Rate Limiter class
class RateLimiter {
  private maxRequestsPerSecond: number;
  private requestTimes: number[];

  constructor(maxRequestsPerSecond = 10) {
    this.maxRequestsPerSecond = maxRequestsPerSecond;
    this.requestTimes = [];
  }

  async throttle(): Promise<void> {
    const now = Date.now();
    const oneSecondAgo = now - 1000;

    // Remove old request times
    this.requestTimes = this.requestTimes.filter((t) => t > oneSecondAgo);

    // If at limit, wait
    if (this.requestTimes.length >= this.maxRequestsPerSecond) {
      const oldestRequest = this.requestTimes[0];
      const waitTime = 1000 - (now - oldestRequest);
      if (waitTime > 0) {
        await new Promise((resolve) => setTimeout(resolve, waitTime));
      }
    }

    this.requestTimes.push(Date.now());
  }
}
