import type { BaseIntegrationAdapter } from '../../../base-integration-adapter'
import { ToolingError } from '../../../../types'
import type { ExecutionContext } from '../../../../../base/types'
import { createHttpError, withRetry, DEFAULT_RETRY_POLICY } from '../../../../../../../platform/resilience'

export interface ClickUpCredentials {
  apiToken?: string
  apiKey?: string
  api_token?: string
  token?: string
}

export interface ClickUpVerifyResult {
  ok: true
  provider: 'clickup'
  teamCount: number
  teams: Array<{
    id: string
    name: string
  }>
}

export interface ClickUpCreateTaskParams {
  listId: string
  name: string
  markdownContent: string
}

export interface ClickUpCreateTaskResult {
  id: string
  url?: string
  name: string
}

interface ClickUpTeamResponse {
  teams?: Array<{
    id?: string | number
    name?: string
  }>
}

interface ClickUpTaskResponse {
  id?: string
  url?: string
  name?: string
}

const CLICKUP_API_BASE_URL = 'https://api.clickup.com/api/v2'

export class ClickUpAdapter implements BaseIntegrationAdapter {
  readonly name = 'clickup'

  async call(
    method: string,
    params: unknown,
    credentials: Record<string, unknown>,
    context?: ExecutionContext
  ): Promise<unknown> {
    if (!this.validateCredentials(credentials)) {
      throw new ToolingError('credentials_invalid', 'Invalid ClickUp credentials', {
        integration: this.name,
        organizationId: context?.organizationId
      })
    }

    const creds = this.normalizeCredentials(credentials as unknown as ClickUpCredentials)

    switch (method) {
      case 'verify':
        return this.verify(creds, context)
      case 'createTask':
        return this.createTask(creds, params as ClickUpCreateTaskParams, context)
      default:
        throw new ToolingError('method_not_found', `Unknown ClickUp method: ${method}`, {
          integration: this.name,
          method,
          organizationId: context?.organizationId
        })
    }
  }

  validateCredentials(credentials: Record<string, unknown>): boolean {
    const creds = credentials as unknown as ClickUpCredentials
    const token = creds.apiToken || creds.apiKey || creds.api_token || creds.token
    return typeof token === 'string' && token.trim().startsWith('pk_')
  }

  async verify(credentials: ClickUpCredentials, context?: ExecutionContext): Promise<ClickUpVerifyResult> {
    const response = await this.request('/team', this.normalizeCredentials(credentials), { method: 'GET' }, 'verify', context)
    const data = (await response.json()) as ClickUpTeamResponse
    const teams = (data.teams ?? []).map((team) => ({
      id: String(team.id ?? ''),
      name: team.name ?? ''
    }))

    return {
      ok: true,
      provider: 'clickup',
      teamCount: teams.length,
      teams: teams.filter((team) => team.id || team.name)
    }
  }

  async createTask(
    credentials: { apiToken: string },
    params: ClickUpCreateTaskParams,
    context?: ExecutionContext
  ): Promise<ClickUpCreateTaskResult> {
    if (!params.listId || !params.name || !params.markdownContent) {
      throw new ToolingError('validation_error', 'listId, name, and markdownContent are required', {
        integration: this.name,
        method: 'createTask',
        organizationId: context?.organizationId
      })
    }

    const response = await this.request(
      `/list/${encodeURIComponent(params.listId)}/task`,
      this.normalizeCredentials(credentials),
      {
        method: 'POST',
        body: JSON.stringify({
          name: params.name,
          markdown_content: params.markdownContent
        })
      },
      'createTask',
      context
    )
    const data = (await response.json()) as ClickUpTaskResponse

    if (!data.id) {
      throw new ToolingError('api_error', 'ClickUp create task response did not include a task ID', {
        integration: this.name,
        method: 'createTask',
        organizationId: context?.organizationId
      })
    }

    return {
      id: data.id,
      url: data.url,
      name: data.name ?? params.name
    }
  }

  private async request(
    path: string,
    credentials: { apiToken: string },
    init: RequestInit,
    method: string,
    context?: ExecutionContext
  ): Promise<Response> {
    if (!this.validateCredentials(credentials as unknown as Record<string, unknown>)) {
      throw new ToolingError('credentials_invalid', 'ClickUp personal API token must start with pk_', {
        integration: this.name,
        method,
        organizationId: context?.organizationId
      })
    }

    return await withRetry(async () => {
      const response = await fetch(`${CLICKUP_API_BASE_URL}${path}`, {
        ...init,
        headers: {
          Authorization: credentials.apiToken,
          'Content-Type': 'application/json',
          ...init.headers
        }
      })

      if (!response.ok) {
        throw await createHttpError(response, {
          integration: this.name,
          method,
          organizationId: context?.organizationId
        })
      }

      return response
    }, DEFAULT_RETRY_POLICY)
  }

  private normalizeCredentials(credentials: ClickUpCredentials): { apiToken: string } {
    const token = credentials.apiToken || credentials.apiKey || credentials.api_token || credentials.token
    if (!token || !token.trim().startsWith('pk_')) {
      throw new ToolingError('credentials_invalid', 'ClickUp personal API token must start with pk_', {
        integration: this.name
      })
    }

    return { apiToken: token.trim() }
  }
}
