/**
 * Jira task adapter — tasks-board-engine. 2-way sync (pushUpdate implemented).
 *
 * Credentials shape: { baseUrl: string, email: string, apiToken: string }
 * Fetches issues assigned to the authenticated user via JQL.
 */
import type { ExternalTask, AdapterCredentials, TaskAdapter } from './types'
import type { TaskObject } from '@zync/types'
import { assertSafeOutboundUrl } from '@zync/utils'

interface JiraIssue {
  id: string
  key: string
  fields: {
    summary: string
    description: unknown
    status: { name: string }
    assignee?: { emailAddress: string }
    duedate?: string
    priority?: { name: string }
  }
}

function basicAuth(email: string, token: string): string {
  return `Basic ${btoa(`${email}:${token}`)}`
}

function assertSafeJiraBaseUrl(baseUrl: string): void {
  assertSafeOutboundUrl(baseUrl)
}

const PRIORITY_MAP: Record<string, 'low' | 'medium' | 'high' | 'urgent'> = {
  Highest: 'urgent',
  High: 'high',
  Medium: 'medium',
  Low: 'low',
  Lowest: 'low',
}

export const jiraAdapter: TaskAdapter = {
  id: 'jira',
  name: 'Jira',

  async fetchTasks(credentials: AdapterCredentials): Promise<ExternalTask[]> {
    const baseUrl = String(credentials['baseUrl'] ?? '').replace(/\/$/, '')
    const email = String(credentials['email'] ?? '')
    const apiToken = String(credentials['apiToken'] ?? '')
    if (!baseUrl || !email || !apiToken) throw new Error('Jira: missing baseUrl, email, or apiToken')

    assertSafeJiraBaseUrl(baseUrl)

    const auth = basicAuth(email, apiToken)
    const jql = encodeURIComponent('assignee = currentUser() ORDER BY updated DESC')
    const url = `${baseUrl}/rest/api/3/search?jql=${jql}&fields=summary,description,status,assignee,duedate,priority&maxResults=100`

    const res = await fetch(url, {
      redirect: 'manual',
      headers: { Authorization: auth, 'Content-Type': 'application/json' },
    })
    if (!res.ok) throw new Error(`Jira API error: ${res.status}`)
    const json = await res.json() as { issues: JiraIssue[] }

    return json.issues.map((issue) => ({
      externalId: issue.key,
      title: issue.fields.summary,
      description: issue.fields.description,
      status: issue.fields.status.name,
      assigneeEmail: issue.fields.assignee?.emailAddress,
      dueDate: issue.fields.duedate ?? undefined,
    }))
  },

  mapToTask(external: ExternalTask, tenantId: string, projectId: string): Partial<TaskObject> {
    return {
      title: external.title,
      description: external.description ?? null,
      source: 'jira',
      external_id: external.externalId,
      due_date: external.dueDate ?? null,
      tenant_id: tenantId,
      project_id: projectId || null,
      priority: 'medium',
      labels: [],
    }
  },

  async pushUpdate(task: TaskObject, credentials: AdapterCredentials): Promise<void> {
    const baseUrl = String(credentials['baseUrl'] ?? '').replace(/\/$/, '')
    const email = String(credentials['email'] ?? '')
    const apiToken = String(credentials['apiToken'] ?? '')
    if (!task.external_id) return
    if (!baseUrl || !email || !apiToken) throw new Error('Jira: missing baseUrl, email, or apiToken')

    assertSafeJiraBaseUrl(baseUrl)

    const auth = basicAuth(email, apiToken)
    const url = `${baseUrl}/rest/api/3/issue/${task.external_id}`
    const body = JSON.stringify({
      fields: {
        summary: task.title,
        ...(task.due_date ? { duedate: task.due_date } : {}),
      },
    })

    const res = await fetch(url, {
      method: 'PUT',
      redirect: 'manual',
      headers: { Authorization: auth, 'Content-Type': 'application/json' },
      body,
    })
    if (!res.ok && res.status !== 204) throw new Error(`Jira pushUpdate failed: ${res.status}`)
  },
}
