import axios, { AxiosInstance, AxiosError } from 'axios';
import type {
  RipplingConfig,
  PaginatedResponse,
  RipplingError,
} from '../types/index.js';

export class RipplingClient {
  private client: AxiosInstance;
  private apiKey?: string;
  private accessToken?: string;

  constructor(config: RipplingConfig) {
    this.apiKey = config.apiKey || process.env.RIPPLING_API_KEY;
    this.accessToken = config.accessToken || process.env.RIPPLING_ACCESS_TOKEN;
    
    const baseURL = config.baseUrl || process.env.RIPPLING_BASE_URL || 'https://api.rippling.com';

    this.client = axios.create({
      baseURL,
      headers: {
        'Content-Type': 'application/json',
        ...(this.accessToken && { Authorization: `Bearer ${this.accessToken}` }),
        ...(this.apiKey && { 'X-API-Key': this.apiKey }),
      },
      timeout: 30000,
    });

    // Response interceptor for error handling
    this.client.interceptors.response.use(
      (response) => response,
      (error: AxiosError) => {
        throw this.handleError(error);
      }
    );
  }

  private handleError(error: AxiosError): RipplingError {
    if (error.response) {
      return {
        message: (error.response.data as any)?.message || error.message,
        code: (error.response.data as any)?.code,
        statusCode: error.response.status,
        details: error.response.data,
      };
    }
    return {
      message: error.message,
      statusCode: 500,
    };
  }

  // Generic GET request
  async get<T>(endpoint: string, params?: Record<string, any>): Promise<T> {
    const response = await this.client.get<T>(endpoint, { params });
    return response.data;
  }

  // Generic POST request
  async post<T>(endpoint: string, data?: any): Promise<T> {
    const response = await this.client.post<T>(endpoint, data);
    return response.data;
  }

  // Generic PUT request
  async put<T>(endpoint: string, data?: any): Promise<T> {
    const response = await this.client.put<T>(endpoint, data);
    return response.data;
  }

  // Generic PATCH request
  async patch<T>(endpoint: string, data?: any): Promise<T> {
    const response = await this.client.patch<T>(endpoint, data);
    return response.data;
  }

  // Generic DELETE request
  async delete<T>(endpoint: string): Promise<T> {
    const response = await this.client.delete<T>(endpoint);
    return response.data;
  }

  // Paginated GET request with cursor-based pagination
  async getPaginated<T>(
    endpoint: string,
    params?: Record<string, any>,
    limit: number = 100
  ): Promise<PaginatedResponse<T>> {
    const response = await this.client.get<PaginatedResponse<T>>(endpoint, {
      params: { ...params, limit },
    });
    return response.data;
  }

  // Get all pages automatically
  async getAllPaginated<T>(
    endpoint: string,
    params?: Record<string, any>,
    limit: number = 100
  ): Promise<T[]> {
    const allData: T[] = [];
    let cursor: string | undefined;
    let hasMore = true;

    while (hasMore) {
      const response = await this.getPaginated<T>(endpoint, { ...params, cursor }, limit);
      allData.push(...response.data);
      cursor = response.nextCursor;
      hasMore = response.hasMore && !!cursor;
    }

    return allData;
  }

  // Employees
  async listEmployees(params?: { status?: string; departmentId?: string; cursor?: string; limit?: number }) {
    return this.getPaginated('/v1/employees', params);
  }

  async getEmployee(id: string) {
    return this.get(`/v1/employees/${id}`);
  }

  async createEmployee(data: any) {
    return this.post('/v1/employees', data);
  }

  async updateEmployee(id: string, data: any) {
    return this.patch(`/v1/employees/${id}`, data);
  }

  async terminateEmployee(id: string, data: { terminationDate: string; reason?: string }) {
    return this.post(`/v1/employees/${id}/terminate`, data);
  }

  async listEmployeeCustomFields() {
    return this.get('/v1/employees/custom-fields');
  }

  async getOrgChart() {
    return this.get('/v1/employees/org-chart');
  }

  // Companies
  async getCompany() {
    return this.get('/v1/company');
  }

  async listDepartments(params?: { cursor?: string; limit?: number }) {
    return this.getPaginated('/v1/departments', params);
  }

  async createDepartment(data: { name: string; parentId?: string; headId?: string }) {
    return this.post('/v1/departments', data);
  }

  async listLocations(params?: { cursor?: string; limit?: number }) {
    return this.getPaginated('/v1/locations', params);
  }

  async listTeams(params?: { cursor?: string; limit?: number }) {
    return this.getPaginated('/v1/teams', params);
  }

  // Payroll
  async listPayRuns(params?: { status?: string; startDate?: string; endDate?: string; cursor?: string; limit?: number }) {
    return this.getPaginated('/v1/payroll/runs', params);
  }

  async getPayRun(id: string) {
    return this.get(`/v1/payroll/runs/${id}`);
  }

  async listPayStatements(params?: { employeeId?: string; payRunId?: string; cursor?: string; limit?: number }) {
    return this.getPaginated('/v1/payroll/statements', params);
  }

  async getPayStatement(id: string) {
    return this.get(`/v1/payroll/statements/${id}`);
  }

  // Time Tracking
  async listTimeEntries(params?: { employeeId?: string; startDate?: string; endDate?: string; cursor?: string; limit?: number }) {
    return this.getPaginated('/v1/time/entries', params);
  }

  async createTimeEntry(data: any) {
    return this.post('/v1/time/entries', data);
  }

  async updateTimeEntry(id: string, data: any) {
    return this.patch(`/v1/time/entries/${id}`, data);
  }

  async deleteTimeEntry(id: string) {
    return this.delete(`/v1/time/entries/${id}`);
  }

  async getTimesheet(id: string) {
    return this.get(`/v1/time/timesheets/${id}`);
  }

  async approveTimesheet(id: string) {
    return this.post(`/v1/time/timesheets/${id}/approve`, {});
  }

  async listTimeOffRequests(params?: { employeeId?: string; status?: string; startDate?: string; endDate?: string; cursor?: string; limit?: number }) {
    return this.getPaginated('/v1/time-off/requests', params);
  }

  async createTimeOffRequest(data: any) {
    return this.post('/v1/time-off/requests', data);
  }

  async approveTimeOffRequest(id: string) {
    return this.post(`/v1/time-off/requests/${id}/approve`, {});
  }

  async denyTimeOffRequest(id: string, reason?: string) {
    return this.post(`/v1/time-off/requests/${id}/deny`, { reason });
  }

  // Benefits
  async listBenefitsPlans(params?: { type?: string; isActive?: boolean; cursor?: string; limit?: number }) {
    return this.getPaginated('/v1/benefits/plans', params);
  }

  async getBenefitsPlan(id: string) {
    return this.get(`/v1/benefits/plans/${id}`);
  }

  async listBenefitsEnrollments(params?: { employeeId?: string; planId?: string; status?: string; cursor?: string; limit?: number }) {
    return this.getPaginated('/v1/benefits/enrollments', params);
  }

  async getBenefitsEnrollment(id: string) {
    return this.get(`/v1/benefits/enrollments/${id}`);
  }

  // ATS (Applicant Tracking)
  async listCandidates(params?: { jobId?: string; stage?: string; cursor?: string; limit?: number }) {
    return this.getPaginated('/v1/ats/candidates', params);
  }

  async getCandidate(id: string) {
    return this.get(`/v1/ats/candidates/${id}`);
  }

  async listJobs(params?: { status?: string; department?: string; cursor?: string; limit?: number }) {
    return this.getPaginated('/v1/ats/jobs', params);
  }

  async getJob(id: string) {
    return this.get(`/v1/ats/jobs/${id}`);
  }

  async listApplications(params?: { candidateId?: string; jobId?: string; status?: string; cursor?: string; limit?: number }) {
    return this.getPaginated('/v1/ats/applications', params);
  }

  async updateApplicationStage(id: string, stage: string) {
    return this.patch(`/v1/ats/applications/${id}`, { stage });
  }

  // Learning
  async listCourses(params?: { category?: string; isRequired?: boolean; cursor?: string; limit?: number }) {
    return this.getPaginated('/v1/learning/courses', params);
  }

  async getCourse(id: string) {
    return this.get(`/v1/learning/courses/${id}`);
  }

  async listCourseAssignments(params?: { employeeId?: string; courseId?: string; status?: string; cursor?: string; limit?: number }) {
    return this.getPaginated('/v1/learning/assignments', params);
  }

  async assignCourse(data: { employeeId: string; courseId: string; dueDate?: string }) {
    return this.post('/v1/learning/assignments', data);
  }

  // Devices
  async listDevices(params?: { type?: string; status?: string; assignedTo?: string; cursor?: string; limit?: number }) {
    return this.getPaginated('/v1/devices', params);
  }

  async getDevice(id: string) {
    return this.get(`/v1/devices/${id}`);
  }

  async listApps(params?: { cursor?: string; limit?: number }) {
    return this.getPaginated('/v1/apps', params);
  }

  async getApp(id: string) {
    return this.get(`/v1/apps/${id}`);
  }

  // Groups
  async listGroups(params?: { type?: string; cursor?: string; limit?: number }) {
    return this.getPaginated('/v1/groups', params);
  }

  async getGroup(id: string) {
    return this.get(`/v1/groups/${id}`);
  }

  async createGroup(data: { name: string; description?: string; type?: string }) {
    return this.post('/v1/groups', data);
  }

  async updateGroup(id: string, data: any) {
    return this.patch(`/v1/groups/${id}`, data);
  }

  async addGroupMember(groupId: string, memberId: string) {
    return this.post(`/v1/groups/${groupId}/members`, { memberId });
  }

  async removeGroupMember(groupId: string, memberId: string) {
    return this.delete(`/v1/groups/${groupId}/members/${memberId}`);
  }

  // Custom Objects
  async listCustomObjects(objectType: string, params?: { cursor?: string; limit?: number }) {
    return this.getPaginated(`/v1/custom-objects/${objectType}`, params);
  }

  async getCustomObject(objectType: string, id: string) {
    return this.get(`/v1/custom-objects/${objectType}/${id}`);
  }

  async createCustomObject(objectType: string, data: any) {
    return this.post(`/v1/custom-objects/${objectType}`, data);
  }

  async updateCustomObject(objectType: string, id: string, data: any) {
    return this.patch(`/v1/custom-objects/${objectType}/${id}`, data);
  }

  async queryCustomObjects(objectType: string, query: any) {
    return this.post(`/v1/custom-objects/${objectType}/query`, query);
  }
}
