/** Calendar API client — thin wrapper over /api/v1/calendar endpoints. */

import { BaseApiClient, getDynamicBaseURL } from './base'

// ── Domain types (co-located with API client) ──────────────────────────

export interface ICalendarEvent {
  id: string
  user_id: string
  title: string
  description: string
  start_time: string // ISO 8601 UTC (KTD-11)
  end_time: string // ISO 8601 UTC
  is_all_day: boolean
  location: string
  event_type_id: string | null
  rrule: string | null // RFC 5545 RRULE string
  source: 'manual' | 'agent' | 'post_extract'
  is_invited: boolean
  conversation_id: string | null
  external_id: string | null
  external_provider: string | null
  last_modified: string
  created_at: string
}

export interface IEventType {
  id: string
  user_id: string
  name: string
  color: string
  is_default: boolean
}

export interface ITag {
  id: string
  user_id: string
  name: string
}

export interface IInvitation {
  id: string
  event_id: string
  inviter_user_id: string
  invitee_email: string
  status: 'pending' | 'accepted' | 'declined' | 'tentative'
  responded_at: string | null
}

export interface IExternalCalendarConfig {
  id: string
  user_id: string
  provider: 'caldav' | 'outlook'
  credentials: string // '***' on read-back; never real credentials
  sync_frequency: number
  sync_scope: string[]
  last_sync: string | null
  sync_token: string | null
}

export interface IUserSearchResult {
  username: string
  email: string
}

// ── Request types ──────────────────────────────────────────────────────

export interface ICreateEventRequest {
  title: string
  start_time: string
  end_time: string
  description?: string
  location?: string
  is_all_day?: boolean
  event_type_id?: string | null
  rrule?: string | null
  tag_ids?: string[]
}

export interface IUpdateEventRequest {
  title?: string
  start_time?: string
  end_time?: string
  description?: string
  location?: string
  is_all_day?: boolean
  event_type_id?: string | null
  rrule?: string | null
}

export interface ICreateEventTypeRequest {
  name: string
  color?: string
}

export interface ICreateTagRequest {
  name: string
}

export interface ICreateInvitationRequest {
  event_id: string
  invitee_email: string
}

export interface ICreateExternalConfigRequest {
  provider: 'caldav' | 'outlook'
  credentials: string // JSON string with provider-specific auth
  sync_frequency?: number
  sync_scope?: string[]
}

// ── Runtime type guard ─────────────────────────────────────────────────

/**
 * Runtime guard for ICalendarEvent — validates the minimum fields required
 * for the calendar store to function safely.
 * ponytail: checks only the keys the store actually reads; full schema
 * validation belongs at the API boundary, not in the WS event handler.
 */
export function isCalendarEvent(value: unknown): value is ICalendarEvent {
  if (typeof value !== 'object' || value === null) return false
  const v = value as Record<string, unknown>
  return (
    typeof v.id === 'string' &&
    typeof v.title === 'string' &&
    typeof v.start_time === 'string' &&
    typeof v.end_time === 'string' &&
    typeof v.is_all_day === 'boolean'
  )
}

// ── API client ─────────────────────────────────────────────────────────

const API_BASE = '/api/v1/calendar'

class CalendarApiClient extends BaseApiClient {
  constructor(baseUrl: string = API_BASE) {
    super(baseUrl)
  }

  /** List events with optional date/type/tag filters */
  async listEvents(
    start?: string,
    end?: string,
    eventTypeId?: string,
    tagId?: string,
  ): Promise<{ success: boolean; events: ICalendarEvent[]; count: number }> {
    const params = new URLSearchParams()
    if (start) params.set('start', start)
    if (end) params.set('end', end)
    if (eventTypeId) params.set('type_id', eventTypeId)
    if (tagId) params.set('tag_id', tagId)
    const qs = params.toString()
    const path = qs ? `/events?${qs}` : '/events'
    return this.request(path, { method: 'GET' })
  }

  /** Create a new event */
  async createEvent(
    data: ICreateEventRequest,
  ): Promise<{ success: boolean; event: ICalendarEvent }> {
    return this.request('/events', {
      method: 'POST',
      body: JSON.stringify(data),
    })
  }

  /** Get a single event by id */
  async getEvent(id: string): Promise<{ success: boolean; event: ICalendarEvent }> {
    return this.request(`/events/${id}`, { method: 'GET' })
  }

  /** Update specific fields of an event (PATCH — partial update) */
  async updateEvent(
    id: string,
    data: IUpdateEventRequest,
  ): Promise<{ success: boolean; event: ICalendarEvent; updated: boolean }> {
    return this.request(`/events/${id}`, {
      method: 'PATCH',
      body: JSON.stringify(data),
    })
  }

  /** Delete an event */
  async deleteEvent(id: string): Promise<{ success: boolean; deleted: boolean }> {
    return this.request(`/events/${id}`, { method: 'DELETE' })
  }

  /** List event types for the current user */
  async listEventTypes(): Promise<{
    success: boolean
    event_types: IEventType[]
    count: number
  }> {
    return this.request('/event-types', { method: 'GET' })
  }

  /** Create an event type */
  async createEventType(
    data: ICreateEventTypeRequest,
  ): Promise<{ success: boolean; event_type: IEventType }> {
    return this.request('/event-types', {
      method: 'POST',
      body: JSON.stringify(data),
    })
  }

  /** List tags for the current user */
  async listTags(): Promise<{ success: boolean; tags: ITag[]; count: number }> {
    return this.request('/tags', { method: 'GET' })
  }

  /** Create a tag */
  async createTag(data: ICreateTagRequest): Promise<{ success: boolean; tag: ITag }> {
    return this.request('/tags', {
      method: 'POST',
      body: JSON.stringify(data),
    })
  }

  /** Import events from an uploaded .ics file (multipart) */
  async importIcs(file: File): Promise<{
    success: boolean
    imported: number
    skipped: number
    [key: string]: unknown
  }> {
    const formData = new FormData()
    formData.append('file', file)
    return this.request('/import-ics', {
      method: 'POST',
      body: formData,
      headers: {}, // Let browser set Content-Type for FormData
    })
  }

  /**
   * Build the export-ics download URL for a date range.
   * Returns an absolute or relative URL the caller can open/download.
   * ponytail: the endpoint returns binary text/calendar, not JSON, so we
   * hand back a URL rather than going through request<T> (which JSON-parses).
   */
  exportIcs(start?: string, end?: string): string {
    const base = getDynamicBaseURL()
    const params = new URLSearchParams()
    if (start) params.set('start', start)
    if (end) params.set('end', end)
    const qs = params.toString()
    const path = qs ? `/api/v1/calendar/export-ics?${qs}` : '/api/v1/calendar/export-ics'
    return base ? `${base}${path}` : path
  }

  /** Create an invitation (invite a user to an event by email) */
  async createInvitation(
    data: ICreateInvitationRequest,
  ): Promise<{ success: boolean; invitation: IInvitation }> {
    return this.request(`/events/${data.event_id}/invitations`, {
      method: 'POST',
      body: JSON.stringify({ invitee_email: data.invitee_email }),
    })
  }

  /** List invitations for the current user */
  async listInvitations(): Promise<{
    success: boolean
    invitations: IInvitation[]
    count: number
  }> {
    return this.request('/invitations', { method: 'GET' })
  }

  /** Respond to an invitation (accept/decline/tentative) */
  async respondToInvitation(
    id: string,
    status: 'accepted' | 'declined' | 'tentative',
  ): Promise<{ success: boolean; status: string }> {
    return this.request(`/invitations/${id}/respond`, {
      method: 'POST',
      body: JSON.stringify({ status }),
    })
  }

  /** Search users by username or email — returns top 10 matches (G5/A3) */
  async searchUsers(q: string): Promise<{
    success: boolean
    users: IUserSearchResult[]
    count: number
  }> {
    return this.request(`/users/search?q=${encodeURIComponent(q)}`, { method: 'GET' })
  }

  /** List external calendar configs for the current user */
  async listExternalConfigs(): Promise<{
    success: boolean
    configs: IExternalCalendarConfig[]
    count: number
  }> {
    return this.request('/external-configs', { method: 'GET' })
  }

  /** Create an external calendar config */
  async createExternalConfig(
    data: ICreateExternalConfigRequest,
  ): Promise<{ success: boolean; config: IExternalCalendarConfig }> {
    return this.request('/external-configs', {
      method: 'POST',
      body: JSON.stringify(data),
    })
  }

  /** Test connection to an external calendar */
  async testExternalConnection(
    id: string,
  ): Promise<{ success: boolean; connected: boolean; error?: string }> {
    return this.request(`/external-configs/${id}/test`, { method: 'POST' })
  }

  /** Trigger an immediate sync for an external calendar config */
  async syncNow(id: string): Promise<{ success: boolean; synced: boolean; error?: string }> {
    return this.request(`/external-configs/${id}/sync`, { method: 'POST' })
  }

  /** Delete an external calendar config */
  async deleteExternalConfig(id: string): Promise<{ success: boolean; deleted: boolean }> {
    return this.request(`/external-configs/${id}`, { method: 'DELETE' })
  }
}

export const calendarApi = new CalendarApiClient()
