/**
 * Intercom API Client
 * API Version: 2.11
 * Base URL: https://api.intercom.io
 */

import axios, { AxiosInstance, AxiosError } from 'axios';
import type {
  Contact,
  Company,
  Conversation,
  Article,
  Collection,
  Section,
  HelpCenter,
  Ticket,
  TicketType,
  Tag,
  Segment,
  Admin,
  Team,
  Note,
  DataAttribute,
  Subscription,
  ListResponse,
  ScrollResponse,
  SearchQuery,
  CreateContactRequest,
  UpdateContactRequest,
  CreateCompanyRequest,
  CreateConversationRequest,
  ReplyConversationRequest,
  CreateTicketRequest,
  CreateNoteRequest,
  Event,
  Message,
  ContactId,
  CompanyId,
  ConversationId,
  ArticleId,
  CollectionId,
  SectionId,
  TicketId,
  TicketTypeId,
  TagId,
  SegmentId,
  AdminId,
  TeamId,
  NoteId,
} from '../types/index.js';

export interface IntercomClientConfig {
  accessToken: string;
  baseURL?: string;
  timeout?: number;
}

export class IntercomClient {
  private client: AxiosInstance;
  private accessToken: string;

  constructor(config: IntercomClientConfig) {
    this.accessToken = config.accessToken;
    
    this.client = axios.create({
      baseURL: config.baseURL || 'https://api.intercom.io',
      timeout: config.timeout || 30000,
      headers: {
        'Authorization': `Bearer ${this.accessToken}`,
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'Intercom-Version': '2.11',
      },
    });

    // Add response interceptor for rate limiting
    this.client.interceptors.response.use(
      (response) => response,
      async (error: AxiosError) => {
        if (error.response?.status === 429) {
          const retryAfter = error.response.headers['retry-after'];
          const waitTime = retryAfter ? parseInt(retryAfter) * 1000 : 5000;
          
          console.warn(`Rate limit hit. Waiting ${waitTime}ms before retry...`);
          await new Promise(resolve => setTimeout(resolve, waitTime));
          
          // Retry the request
          return this.client.request(error.config!);
        }
        throw error;
      }
    );
  }

  // ============================================================================
  // CONTACTS
  // ============================================================================

  async createContact(data: CreateContactRequest): Promise<Contact> {
    const response = await this.client.post<Contact>('/contacts', data);
    return response.data;
  }

  async getContact(id: ContactId): Promise<Contact> {
    const response = await this.client.get<Contact>(`/contacts/${id}`);
    return response.data;
  }

  async updateContact(id: ContactId, data: UpdateContactRequest): Promise<Contact> {
    const response = await this.client.put<Contact>(`/contacts/${id}`, data);
    return response.data;
  }

  async deleteContact(id: ContactId): Promise<{ id: ContactId; deleted: boolean }> {
    const response = await this.client.delete(`/contacts/${id}`);
    return response.data;
  }

  async listContacts(params?: {
    per_page?: number;
    starting_after?: string;
  }): Promise<ListResponse<Contact>> {
    const response = await this.client.get<ListResponse<Contact>>('/contacts', {
      params,
    });
    return response.data;
  }

  async searchContacts(query: SearchQuery): Promise<ListResponse<Contact>> {
    const response = await this.client.post<ListResponse<Contact>>(
      '/contacts/search',
      query
    );
    return response.data;
  }

  async scrollContacts(scrollParam?: string): Promise<ScrollResponse<Contact>> {
    const response = await this.client.get<ScrollResponse<Contact>>('/contacts/scroll', {
      params: scrollParam ? { scroll_param: scrollParam } : undefined,
    });
    return response.data;
  }

  async mergeContacts(from: ContactId, into: ContactId): Promise<Contact> {
    const response = await this.client.post<Contact>('/contacts/merge', {
      from,
      into,
    });
    return response.data;
  }

  async archiveContact(id: ContactId): Promise<Contact> {
    const response = await this.client.post<Contact>(`/contacts/${id}/archive`);
    return response.data;
  }

  async unarchiveContact(id: ContactId): Promise<Contact> {
    const response = await this.client.post<Contact>(`/contacts/${id}/unarchive`);
    return response.data;
  }

  // ============================================================================
  // COMPANIES
  // ============================================================================

  async createCompany(data: CreateCompanyRequest): Promise<Company> {
    const response = await this.client.post<Company>('/companies', data);
    return response.data;
  }

  async getCompany(id: CompanyId): Promise<Company> {
    const response = await this.client.get<Company>(`/companies/${id}`);
    return response.data;
  }

  async updateCompany(id: CompanyId, data: Partial<CreateCompanyRequest>): Promise<Company> {
    const response = await this.client.put<Company>(`/companies/${id}`, data);
    return response.data;
  }

  async listCompanies(params?: {
    per_page?: number;
    starting_after?: string;
  }): Promise<ListResponse<Company>> {
    const response = await this.client.get<ListResponse<Company>>('/companies', {
      params,
    });
    return response.data;
  }

  async scrollCompanies(scrollParam?: string): Promise<ScrollResponse<Company>> {
    const response = await this.client.get<ScrollResponse<Company>>('/companies/scroll', {
      params: scrollParam ? { scroll_param: scrollParam } : undefined,
    });
    return response.data;
  }

  async attachContactToCompany(contactId: ContactId, companyId: CompanyId): Promise<Company> {
    const response = await this.client.post<Company>(
      `/contacts/${contactId}/companies`,
      { id: companyId }
    );
    return response.data;
  }

  async detachContactFromCompany(contactId: ContactId, companyId: CompanyId): Promise<Company> {
    const response = await this.client.delete<Company>(
      `/contacts/${contactId}/companies/${companyId}`
    );
    return response.data;
  }

  // ============================================================================
  // CONVERSATIONS
  // ============================================================================

  async createConversation(data: CreateConversationRequest): Promise<Conversation> {
    const response = await this.client.post<Conversation>('/conversations', data);
    return response.data;
  }

  async getConversation(id: ConversationId): Promise<Conversation> {
    const response = await this.client.get<Conversation>(`/conversations/${id}`);
    return response.data;
  }

  async listConversations(params?: {
    per_page?: number;
    starting_after?: string;
  }): Promise<ListResponse<Conversation>> {
    const response = await this.client.get<ListResponse<Conversation>>('/conversations', {
      params,
    });
    return response.data;
  }

  async searchConversations(query: SearchQuery): Promise<ListResponse<Conversation>> {
    const response = await this.client.post<ListResponse<Conversation>>(
      '/conversations/search',
      query
    );
    return response.data;
  }

  async replyToConversation(
    id: ConversationId,
    data: ReplyConversationRequest
  ): Promise<Conversation> {
    const response = await this.client.post<Conversation>(
      `/conversations/${id}/reply`,
      data
    );
    return response.data;
  }

  async assignConversation(
    id: ConversationId,
    assignee: { type: 'admin' | 'team'; id: AdminId | TeamId; admin_id?: AdminId }
  ): Promise<Conversation> {
    const response = await this.client.post<Conversation>(
      `/conversations/${id}/parts`,
      {
        message_type: 'assignment',
        ...assignee,
      }
    );
    return response.data;
  }

  async snoozeConversation(
    id: ConversationId,
    snoozedUntil: number
  ): Promise<Conversation> {
    const response = await this.client.post<Conversation>(
      `/conversations/${id}/parts`,
      {
        message_type: 'snoozed',
        type: 'admin',
        snoozed_until: snoozedUntil,
      }
    );
    return response.data;
  }

  async closeConversation(id: ConversationId, adminId: AdminId): Promise<Conversation> {
    const response = await this.client.post<Conversation>(
      `/conversations/${id}/parts`,
      {
        message_type: 'close',
        type: 'admin',
        admin_id: adminId,
      }
    );
    return response.data;
  }

  async openConversation(id: ConversationId, adminId: AdminId): Promise<Conversation> {
    const response = await this.client.post<Conversation>(
      `/conversations/${id}/parts`,
      {
        message_type: 'open',
        type: 'admin',
        admin_id: adminId,
      }
    );
    return response.data;
  }

  async attachTagToConversation(id: ConversationId, tagId: TagId, adminId: AdminId): Promise<Conversation> {
    const response = await this.client.post<Conversation>(
      `/conversations/${id}/tags`,
      {
        id: tagId,
        admin_id: adminId,
      }
    );
    return response.data;
  }

  async detachTagFromConversation(id: ConversationId, tagId: TagId, adminId: AdminId): Promise<Conversation> {
    const response = await this.client.delete<Conversation>(
      `/conversations/${id}/tags/${tagId}`,
      {
        data: { admin_id: adminId },
      }
    );
    return response.data;
  }

  // ============================================================================
  // ARTICLES
  // ============================================================================

  async createArticle(data: {
    title: string;
    description?: string;
    body?: string;
    author_id: AdminId;
    state?: 'published' | 'draft';
    parent_id?: CollectionId | SectionId;
    parent_type?: 'collection' | 'section';
  }): Promise<Article> {
    const response = await this.client.post<Article>('/articles', data);
    return response.data;
  }

  async getArticle(id: ArticleId): Promise<Article> {
    const response = await this.client.get<Article>(`/articles/${id}`);
    return response.data;
  }

  async updateArticle(id: ArticleId, data: Partial<{
    title: string;
    description: string;
    body: string;
    author_id: AdminId;
    state: 'published' | 'draft';
    parent_id: CollectionId | SectionId;
    parent_type: 'collection' | 'section';
  }>): Promise<Article> {
    const response = await this.client.put<Article>(`/articles/${id}`, data);
    return response.data;
  }

  async deleteArticle(id: ArticleId): Promise<{ id: ArticleId; deleted: boolean }> {
    const response = await this.client.delete(`/articles/${id}`);
    return response.data;
  }

  async listArticles(params?: {
    per_page?: number;
    page?: number;
  }): Promise<ListResponse<Article>> {
    const response = await this.client.get<ListResponse<Article>>('/articles', {
      params,
    });
    return response.data;
  }

  // ============================================================================
  // HELP CENTER
  // ============================================================================

  async listHelpCenters(): Promise<ListResponse<HelpCenter>> {
    const response = await this.client.get<ListResponse<HelpCenter>>('/help_center/help_centers');
    return response.data;
  }

  async getHelpCenter(id: string): Promise<HelpCenter> {
    const response = await this.client.get<HelpCenter>(`/help_center/help_centers/${id}`);
    return response.data;
  }

  async listCollections(params?: {
    per_page?: number;
    page?: number;
  }): Promise<ListResponse<Collection>> {
    const response = await this.client.get<ListResponse<Collection>>(
      '/help_center/collections',
      { params }
    );
    return response.data;
  }

  async getCollection(id: CollectionId): Promise<Collection> {
    const response = await this.client.get<Collection>(
      `/help_center/collections/${id}`
    );
    return response.data;
  }

  async createCollection(data: {
    name: string;
    description?: string;
    parent_id?: string;
  }): Promise<Collection> {
    const response = await this.client.post<Collection>(
      '/help_center/collections',
      data
    );
    return response.data;
  }

  async updateCollection(
    id: CollectionId,
    data: Partial<{ name: string; description: string }>
  ): Promise<Collection> {
    const response = await this.client.put<Collection>(
      `/help_center/collections/${id}`,
      data
    );
    return response.data;
  }

  async deleteCollection(id: CollectionId): Promise<{ id: CollectionId; deleted: boolean }> {
    const response = await this.client.delete(`/help_center/collections/${id}`);
    return response.data;
  }

  async listSections(collectionId: CollectionId): Promise<ListResponse<Section>> {
    const response = await this.client.get<ListResponse<Section>>(
      `/help_center/collections/${collectionId}/sections`
    );
    return response.data;
  }

  async getSection(id: SectionId): Promise<Section> {
    const response = await this.client.get<Section>(`/help_center/sections/${id}`);
    return response.data;
  }

  async createSection(collectionId: CollectionId, data: {
    name: string;
  }): Promise<Section> {
    const response = await this.client.post<Section>(
      `/help_center/collections/${collectionId}/sections`,
      data
    );
    return response.data;
  }

  // ============================================================================
  // TICKETS
  // ============================================================================

  async createTicket(data: CreateTicketRequest): Promise<Ticket> {
    const response = await this.client.post<Ticket>('/tickets', data);
    return response.data;
  }

  async getTicket(id: TicketId): Promise<Ticket> {
    const response = await this.client.get<Ticket>(`/tickets/${id}`);
    return response.data;
  }

  async updateTicket(id: TicketId, data: Partial<CreateTicketRequest>): Promise<Ticket> {
    const response = await this.client.put<Ticket>(`/tickets/${id}`, data);
    return response.data;
  }

  async listTickets(params?: {
    per_page?: number;
    page?: number;
  }): Promise<ListResponse<Ticket>> {
    const response = await this.client.get<ListResponse<Ticket>>('/tickets', {
      params,
    });
    return response.data;
  }

  async searchTickets(query: SearchQuery): Promise<ListResponse<Ticket>> {
    const response = await this.client.post<ListResponse<Ticket>>(
      '/tickets/search',
      query
    );
    return response.data;
  }

  async listTicketTypes(): Promise<ListResponse<TicketType>> {
    const response = await this.client.get<ListResponse<TicketType>>('/ticket_types');
    return response.data;
  }

  async getTicketType(id: TicketTypeId): Promise<TicketType> {
    const response = await this.client.get<TicketType>(`/ticket_types/${id}`);
    return response.data;
  }

  // ============================================================================
  // TAGS
  // ============================================================================

  async createTag(name: string): Promise<Tag> {
    const response = await this.client.post<Tag>('/tags', { name });
    return response.data;
  }

  async getTag(id: TagId): Promise<Tag> {
    const response = await this.client.get<Tag>(`/tags/${id}`);
    return response.data;
  }

  async listTags(): Promise<ListResponse<Tag>> {
    const response = await this.client.get<ListResponse<Tag>>('/tags');
    return response.data;
  }

  async deleteTag(id: TagId): Promise<{ id: TagId; deleted: boolean }> {
    const response = await this.client.delete(`/tags/${id}`);
    return response.data;
  }

  async tagContact(contactId: ContactId, tagId: TagId): Promise<Tag> {
    const response = await this.client.post<Tag>(`/contacts/${contactId}/tags`, {
      id: tagId,
    });
    return response.data;
  }

  async untagContact(contactId: ContactId, tagId: TagId): Promise<Tag> {
    const response = await this.client.delete<Tag>(
      `/contacts/${contactId}/tags/${tagId}`
    );
    return response.data;
  }

  async tagCompany(companyId: CompanyId, tagId: TagId): Promise<Tag> {
    const response = await this.client.post<Tag>(`/companies/${companyId}/tags`, {
      id: tagId,
    });
    return response.data;
  }

  async untagCompany(companyId: CompanyId, tagId: TagId): Promise<Tag> {
    const response = await this.client.delete<Tag>(
      `/companies/${companyId}/tags/${tagId}`
    );
    return response.data;
  }

  // ============================================================================
  // SEGMENTS
  // ============================================================================

  async listSegments(params?: {
    include_count?: boolean;
  }): Promise<ListResponse<Segment>> {
    const response = await this.client.get<ListResponse<Segment>>('/segments', {
      params,
    });
    return response.data;
  }

  async getSegment(id: SegmentId): Promise<Segment> {
    const response = await this.client.get<Segment>(`/segments/${id}`);
    return response.data;
  }

  // ============================================================================
  // EVENTS
  // ============================================================================

  async submitEvent(event: Event): Promise<{ type: 'event'; success: boolean }> {
    const response = await this.client.post<{ type: 'event'; success: boolean }>(
      '/events',
      event
    );
    return response.data;
  }

  async listEventSummaries(params: {
    user_id?: string;
    email?: string;
    type?: 'user' | 'company';
    count?: number;
  }): Promise<{ type: 'event.summary'; events: Array<{ name: string; count: number; last: number }> }> {
    const response = await this.client.get('/events/summaries', { params });
    return response.data;
  }

  // ============================================================================
  // MESSAGES
  // ============================================================================

  async sendMessage(message: Message): Promise<{ type: 'message'; id: string }> {
    const response = await this.client.post<{ type: 'message'; id: string }>(
      '/messages',
      message
    );
    return response.data;
  }

  // ============================================================================
  // ADMINS & TEAMS
  // ============================================================================

  async listAdmins(): Promise<ListResponse<Admin>> {
    const response = await this.client.get<ListResponse<Admin>>('/admins');
    return response.data;
  }

  async getAdmin(id: AdminId): Promise<Admin> {
    const response = await this.client.get<Admin>(`/admins/${id}`);
    return response.data;
  }

  async listTeams(): Promise<ListResponse<Team>> {
    const response = await this.client.get<ListResponse<Team>>('/teams');
    return response.data;
  }

  async getTeam(id: TeamId): Promise<Team> {
    const response = await this.client.get<Team>(`/teams/${id}`);
    return response.data;
  }

  // ============================================================================
  // NOTES
  // ============================================================================

  async createNote(data: CreateNoteRequest): Promise<Note> {
    const response = await this.client.post<Note>('/notes', data);
    return response.data;
  }

  async getNote(id: NoteId): Promise<Note> {
    const response = await this.client.get<Note>(`/notes/${id}`);
    return response.data;
  }

  async listNotes(contactId: ContactId): Promise<ListResponse<Note>> {
    const response = await this.client.get<ListResponse<Note>>(
      `/contacts/${contactId}/notes`
    );
    return response.data;
  }

  // ============================================================================
  // DATA ATTRIBUTES
  // ============================================================================

  async listDataAttributes(params?: {
    model?: 'contact' | 'company' | 'conversation';
    include_archived?: boolean;
  }): Promise<ListResponse<DataAttribute>> {
    const response = await this.client.get<ListResponse<DataAttribute>>(
      '/data_attributes',
      { params }
    );
    return response.data;
  }

  async createDataAttribute(data: {
    name: string;
    model: 'contact' | 'company' | 'conversation';
    data_type: string;
    description?: string;
    options?: string[];
  }): Promise<DataAttribute> {
    const response = await this.client.post<DataAttribute>('/data_attributes', data);
    return response.data;
  }

  async updateDataAttribute(id: string, data: {
    archived?: boolean;
    description?: string;
    options?: string[];
  }): Promise<DataAttribute> {
    const response = await this.client.put<DataAttribute>(`/data_attributes/${id}`, data);
    return response.data;
  }

  // ============================================================================
  // SUBSCRIPTIONS
  // ============================================================================

  async listSubscriptions(): Promise<ListResponse<Subscription>> {
    const response = await this.client.get<ListResponse<Subscription>>('/subscription_types');
    return response.data;
  }
}
