/**
 *
 * Tool Services Registry
 * Global singleton registry for tool services (command queue, task scheduler, integrations)
 *
 * Pattern: Global registry with initialization safety
 * - Initialized once at server startup (apps/api/src/main.ts)
 * - Tools import getToolServices() directly (no dependency injection)
 * - Fail-fast behavior if not initialized
 */

import type { CreateTaskParams, Task } from '../../../commands/queue/types'
import type { CreateScheduleInput, TaskSchedule } from '../../scheduler/types'
import type { CreateNotificationParams, Notification } from '../../../operations/notifications/types'
import type { ILeadService } from './lead-service-types'
import type { ExecutionContext } from '../base/types'
import type {
  ProjectRow,
  ProjectWithCounts,
  ProjectDetail,
  MilestoneRow,
  TaskRow,
  NoteRow
} from '../../../business/projects'
import type { DealListItem, DealDetail, ListDealsQuery } from '../../../business/acquisition'
import type { RecentActivityEntry } from '../../../business/sales/api-schemas'
import type { Database } from '../../../supabase/database.types'

/**
 * Service interfaces for platform tools
 *
 * IMPORTANT: These interfaces define the contract that services must implement
 * Actual service implementations live in apps/api/
 * TypeScript structural typing ensures compatibility without circular dependencies
 */
export interface ICommandQueueService {
  /**
   * Create approval/rejection task
   * @see CommandQueueService.createTask (apps/api/src/command-queue/service.ts:23)
   */
  createTask(params: CreateTaskParams): Promise<Task>

  /**
   * Delete pending HITL tasks matching metadata filter
   * Uses JSONB containment (.contains()) for metadata-based batch deletion
   * @see CommandQueueService.deleteByMetadata (apps/api/src/commands/queue/service.ts)
   */
  deleteByMetadata(
    organizationId: string,
    metadata: Record<string, unknown>,
    status?: string
  ): Promise<{ deleted: number }>
}

export interface ITaskSchedulerService {
  /**
   * Create a unified schedule
   * @see TaskScheduleService.createSchedule (apps/api/src/task-scheduler/service.ts:190)
   */
  createSchedule(input: CreateScheduleInput): Promise<TaskSchedule>

  /**
   * Update anchor time for a relative schedule (used for rescheduling)
   * @see TaskScheduleService.updateAnchor (apps/api/src/task-scheduler/service.ts)
   */
  updateAnchor(id: string, organizationId: string, anchorAt: string): Promise<TaskSchedule>

  /**
   * Delete (cancel) a schedule
   * @see TaskScheduleService.deleteSchedule (apps/api/src/task-scheduler/service.ts)
   */
  deleteSchedule(id: string, organizationId: string): Promise<void>

  /**
   * Find schedule by idempotency key
   * @see TaskScheduleService.findByIdempotencyKey (apps/api/src/task-scheduler/service.ts)
   */
  findByIdempotencyKey(organizationId: string, idempotencyKey: string): Promise<TaskSchedule | null>

  /**
   * Delete (cancel) schedule by idempotency key
   * Finds schedule by key and cancels it. No-op if schedule not found.
   * @see TaskScheduleService.deleteScheduleByIdempotencyKey (apps/api/src/task-scheduler/service.ts)
   */
  deleteScheduleByIdempotencyKey(idempotencyKey: string, organizationId: string): Promise<void>

  /**
   * Cancel a schedule by idempotency key (soft delete)
   * Finds schedule by key and sets status to 'cancelled'. No-op if not found.
   * @see TaskScheduleService.cancelScheduleByIdempotencyKey (apps/api/src/execution/task-scheduler/service.ts)
   */
  cancelScheduleByIdempotencyKey(idempotencyKey: string, organizationId: string): Promise<{ cancelled: boolean }>

  /**
   * List schedules with optional metadata filtering
   * Supports JSONB containment for metadata-based queries
   * @see TaskScheduleService.listSchedules (apps/api/src/execution/task-scheduler/service.ts)
   */
  listSchedules(params: {
    organizationId: string
    targetResourceId?: string
    status?: string
    metadata?: Record<string, unknown>
    limit?: number
    offset?: number
  }): Promise<{ schedules: TaskSchedule[]; total: number }>

  /**
   * Get a single schedule by ID
   * @see TaskScheduleService.getSchedule (apps/api/src/execution/task-scheduler/service.ts)
   */
  getSchedule(id: string, organizationId: string): Promise<TaskSchedule | null>

  /**
   * Cancel a schedule (soft delete - sets status to 'cancelled')
   * @see TaskScheduleService.cancelSchedule (apps/api/src/execution/task-scheduler/service.ts)
   */
  cancelSchedule(id: string, organizationId: string): Promise<TaskSchedule>

  /**
   * Cancel all schedules matching metadata filter (batch soft delete)
   * Uses JSONB containment (.contains()) for metadata-based filtering
   * @see TaskScheduleService.cancelSchedulesByMetadata (apps/api/src/execution/task-scheduler/service.ts)
   */
  cancelSchedulesByMetadata(
    organizationId: string,
    metadata: Record<string, unknown>,
    status?: string
  ): Promise<{ cancelled: number }>
}

export interface INotificationsService {
  /**
   * Create platform notification for user
   * @see NotificationService.create (apps/api/src/notifications/service.ts:17)
   */
  create(params: CreateNotificationParams): Promise<Notification>
}

export interface ICrmService {
  getRecentActivity(organizationId: string, limit: number): Promise<RecentActivityEntry[]>
  listDeals(organizationId: string, filters?: Partial<ListDealsQuery>): Promise<DealListItem[]>
  getDeal(dealId: string, organizationId: string): Promise<DealDetail | null>
  getDealByEmail(email: string, organizationId: string): Promise<DealDetail | null>
  createDealNote(
    params: import('./lead-service-types').CreateDealNoteParams
  ): Promise<import('./lead-service-types').AcqDealNote>
  listDealNotes(
    params: import('./lead-service-types').ListDealNotesParams
  ): Promise<import('./lead-service-types').AcqDealNote[]>
  createDealTask(
    params: import('./lead-service-types').CreateDealTaskParams
  ): Promise<import('./lead-service-types').AcqDealTask>
  listDealTasks(
    params: import('./lead-service-types').ListDealTasksParams
  ): Promise<import('./lead-service-types').AcqDealTask[]>
  listDealTasksDue(
    params: import('./lead-service-types').ListDealTasksDueParams
  ): Promise<import('./lead-service-types').AcqDealTask[]>
  completeDealTask(
    params: import('./lead-service-types').CompleteDealTaskParams
  ): Promise<import('./lead-service-types').AcqDealTask>
  recordActivity(params: import('./lead-service-types').RecordDealActivityParams): Promise<void>
  deleteDeal(params: import('./lead-service-types').DeleteDealParams): Promise<void>
}

type ProjectInsert = Database['public']['Tables']['prj_projects']['Insert']
type ProjectUpdate = Database['public']['Tables']['prj_projects']['Update']
type MilestoneInsert = Database['public']['Tables']['prj_milestones']['Insert']
type MilestoneUpdate = Database['public']['Tables']['prj_milestones']['Update']
type TaskInsert = Database['public']['Tables']['prj_tasks']['Insert']
type TaskUpdate = Database['public']['Tables']['prj_tasks']['Update']
type NoteInsert = Database['public']['Tables']['prj_notes']['Insert']
type NoteUpdate = Database['public']['Tables']['prj_notes']['Update']
type TaskResumeContextRow = Pick<TaskRow, 'id' | 'resume_context' | 'updated_at'>
type DeleteEntityResult = { id: string; project_id: string }

export interface IProjectsService {
  listProjects(
    organizationId: string,
    filters?: {
      kind?: string
      status?: string
    }
  ): Promise<ProjectWithCounts[]>
  getProject(id: string, organizationId: string): Promise<ProjectDetail | null>
  createProject(input: Omit<ProjectInsert, 'organization_id'> & { organizationId: string }): Promise<ProjectRow>
  updateProject(
    id: string,
    organizationId: string,
    input: Omit<ProjectUpdate, 'organization_id'>
  ): Promise<ProjectRow | null>
  deleteProject(id: string, organizationId: string): Promise<boolean>
  listMilestones(projectId: string, organizationId: string): Promise<MilestoneRow[]>
  createMilestone(
    input: Omit<MilestoneInsert, 'organization_id' | 'project_id'> & {
      organizationId: string
      projectId: string
    }
  ): Promise<MilestoneRow>
  updateMilestone(
    id: string,
    organizationId: string,
    input: Omit<MilestoneUpdate, 'organization_id'>
  ): Promise<MilestoneRow | null>
  deleteMilestone(id: string, organizationId: string): Promise<DeleteEntityResult | null>
  listTasks(
    projectId: string,
    organizationId: string,
    filters?: {
      status?: string
      milestone_id?: string
      parent_task_id?: string
    }
  ): Promise<TaskRow[]>
  getTask(id: string, organizationId: string): Promise<TaskRow | null>
  createTask(input: Omit<TaskInsert, 'organization_id'> & { organizationId: string }): Promise<TaskRow>
  updateTask(id: string, organizationId: string, input: Omit<TaskUpdate, 'organization_id'>): Promise<TaskRow | null>
  deleteTask(id: string, organizationId: string): Promise<DeleteEntityResult | null>
  mergeTaskResumeContext(
    id: string,
    organizationId: string,
    incoming: Record<string, unknown>
  ): Promise<TaskResumeContextRow | null>
  listNotes(projectId: string, organizationId: string): Promise<NoteRow[]>
  createNote(input: Omit<NoteInsert, 'organization_id'> & { organizationId: string }): Promise<NoteRow>
  updateNote(id: string, organizationId: string, input: Omit<NoteUpdate, 'organization_id'>): Promise<NoteRow | null>
  deleteNote(id: string, organizationId: string): Promise<DeleteEntityResult | null>
}

// =============================================================================
// Platform Email Service Types
// =============================================================================

/**
 * Parameters for sending platform emails
 * Supports three targeting modes: explicit userIds, role-based, or broadcast
 */
export interface SendPlatformEmailParams {
  organizationId: string

  // Targeting (exactly one required)
  userIds?: string[] // Explicit: specific users
  targetRole?: string // Role-based: users with role
  targetAll?: boolean // Broadcast: all org members

  // Content
  subject: string
  html?: string
  text?: string

  // Optional
  replyTo?: string
  tags?: Array<{ name: string; value: string }>
}

/**
 * Result of platform email send operation
 * Follows partial success pattern (similar to BatchProcessResult)
 */
export interface EmailResult {
  sent: number // Successfully sent
  failed: number // Failed to send
  errors?: Array<{
    // Only if failed > 0
    userId: string
    email: string
    error: string
  }>
}

/**
 * Platform email service interface
 * Sends transactional emails from notifications@elevasis.io using platform Resend credentials
 *
 * Implementation: apps/api/src/email/service.ts (EmailService class)
 *
 * Multi-tenancy: All operations require organizationId for tenant isolation.
 * User targeting queries filtered by organizationId via org_memberships join.
 */
export interface IEmailService {
  /**
   * Send platform email to targeted users within an organization
   *
   * Targeting modes (exactly one required):
   * - userIds: Send to specific users (verified against org membership)
   * - targetRole: Send to users with specific role
   * - targetAll: Send to all org members
   *
   * @param params - Email parameters including targeting and content
   * @returns Result with sent/failed counts and optional errors array
   * @throws Error if no targeting specified or all userIds invalid
   * @see EmailService.send (apps/api/src/email/service.ts)
   */
  send(params: SendPlatformEmailParams): Promise<EmailResult>
}

/**
 * Credentials service interface
 * Provides access to organization credentials for integration tools
 *
 * Implementation: apps/api/src/multi-tenancy/credentials/service.ts (CredentialsService class)
 *
 * Note: Integration tools only need getCredential() method.
 * Other methods (createCredential, listCredentials, deleteCredential) exist in the implementation
 * but are only used by API handlers.
 */
export interface ICredentialsService {
  /**
   * Get decrypted credential for organization and integration
   * @param organizationId - Organization ID from ExecutionContext
   * @param credentialName - Integration name (e.g., 'gmail', 'slack')
   * @param environment - Optional environment filter ('development' | 'production')
   * @returns Decrypted credential object or null if not found
   * @see CredentialsService.getCredential (apps/api/src/multi-tenancy/credentials/service.ts:41-57)
   */
  getCredential(
    organizationId: string,
    credentialName: string,
    environment?: 'development' | 'production'
  ): Promise<Record<string, unknown> | null>
}

// =============================================================================
// Platform Storage Service Types
// =============================================================================

/**
 * Parameters for storage upload
 */
export interface StorageUploadParams {
  organizationId: string
  bucket: string
  path: string
  file: Buffer | Blob
  contentType: string
  upsert?: boolean
}

/**
 * Parameters for storage download/delete
 */
export interface StorageDownloadParams {
  organizationId: string
  bucket: string
  path: string
}

/**
 * Parameters for creating signed URL
 */
export interface StorageSignedUrlParams {
  organizationId: string
  bucket: string
  path: string
  expiresIn: number // seconds
}

/**
 * Result of storage upload operation
 */
export interface StorageUploadResult {
  path: string
  fullPath: string
  size: number
}

/**
 * Result of signed URL creation
 */
export interface StorageSignedUrlResult {
  signedUrl: string
  path: string
  expiresAt: Date
}

/**
 * Storage service interface for platform storage tools
 * Provides org-scoped file storage using Supabase Storage
 *
 * Implementation: apps/api/src/storage/service.ts (StorageService class)
 *
 * Multi-tenancy: All paths prefixed with organizationId for tenant isolation.
 * RLS policies enforce access at storage.objects level.
 */
export interface IStorageService {
  /**
   * Upload file to org-scoped path
   * Path format: {organizationId}/{path}
   * @see StorageService.upload (apps/api/src/storage/service.ts)
   */
  upload(params: StorageUploadParams): Promise<StorageUploadResult>

  /**
   * Download file from org-scoped path
   * @see StorageService.download (apps/api/src/storage/service.ts)
   */
  download(params: StorageDownloadParams): Promise<Blob>

  /**
   * Create signed URL for temporary access
   * @see StorageService.createSignedUrl (apps/api/src/storage/service.ts)
   */
  createSignedUrl(params: StorageSignedUrlParams): Promise<StorageSignedUrlResult>

  /**
   * Delete file from org-scoped path
   * @see StorageService.delete (apps/api/src/storage/service.ts)
   */
  delete(params: StorageDownloadParams): Promise<void>

  /**
   * List files in org-scoped path
   * @see StorageService.list (apps/api/src/storage/service.ts)
   */
  list(params: { organizationId: string; bucket: string; prefix?: string }): Promise<string[]>
}

/**
 * Integration service interface
 * Provides access to external API integrations (Gmail, Slack, etc.)
 *
 * Implementation: packages/core/src/execution-engine/tools/integration/service.ts
 *
 * Interface Segregation Principle: Only exposes call() method used by consumers.
 * Internal state (adapters, rateLimiter, retryPolicy) hidden from interface.
 */
export interface IIntegrationService {
  /**
   * Call integration method with full orchestration
   *
   * Orchestrates: rate limiting, credential retrieval, validation, retry
   *
   * @param integration - Integration adapter type (e.g., 'gmail', 'slack')
   * @param method - Method name (e.g., 'sendEmail', 'postMessage')
   * @param params - Method parameters
   * @param credentialName - Credential name from credentials table
   * @param context - Execution context (required for multi-tenant isolation)
   * @returns Method result
   * @throws ToolingError if call fails
   */
  call(
    integration: string,
    method: string,
    params: unknown,
    credentialName: string,
    context?: ExecutionContext
  ): Promise<unknown>
}

// =============================================================================
// PDF Service Types
// =============================================================================

/**
 * PDF document structure (schema-driven)
 * Full type definition lives in packages/core/src/pdf/types.ts
 * Using Record<string, unknown> here to avoid circular dependencies
 */
export interface PdfRenderParams {
  organizationId: string
  /** Document structure with pages and content blocks */
  document: Record<string, unknown>
  /** Optional theme override (partial merge with default theme) */
  theme?: Record<string, unknown>
  /** Storage destination for rendered PDF */
  storage: {
    bucket: string
    path: string
  }
}

/**
 * Result of PDF render operation
 */
export interface PdfRenderResult {
  success: boolean
  /** Signed URL for accessing the rendered PDF */
  pdfUrl: string
  /** PDF file size in bytes */
  size: number
}

/**
 * PDF service interface for rendering documents
 * Provides pdfmake-based document rendering with optional storage upload
 *
 * Implementation: packages/core/src/pdf/server/pdfmake-service.ts (PdfMakeService class)
 *
 * Multi-tenancy: organizationId used for storage path prefixing.
 * All PDFs stored under org-scoped paths via StorageService.
 */
export interface IPdfService {
  /**
   * Render PDF document and upload to storage
   *
   * Flow:
   * 1. Parse document structure
   * 2. Merge theme with defaults
   * 3. Render via pdfmake
   * 4. Upload to storage via StorageService
   * 5. Return signed URL
   *
   * @param params - Render parameters including document, theme, and storage destination
   * @returns Result with signed URL and file size
   * @throws Error if rendering fails or storage upload fails
   * @see PdfMakeService.render (packages/core/src/pdf/server/pdfmake-service.ts)
   */
  render(params: PdfRenderParams): Promise<PdfRenderResult>

  /**
   * Render PDF document to buffer (no storage upload)
   *
   * Use this for testing or when you need to handle storage upload separately.
   *
   * @param params - Render parameters (organizationId, document, optional theme)
   * @returns Buffer containing the PDF
   * @throws Error if rendering fails
   * @see PdfMakeService.renderToBuffer (packages/core/src/pdf/server/pdfmake-service.ts)
   */
  renderToBuffer(params: Omit<PdfRenderParams, 'storage'>): Promise<Buffer>
}

// =============================================================================
// Lead Service Types (extracted to lead-service-types.ts)
// =============================================================================
export {
  type AcqList,
  type AcqCompany,
  type AcqContact,
  type PaginationParams,
  type PaginatedResult,
  type CreateListParams,
  type UpdateListParams,
  type CreateCompanyParams,
  type UpdateCompanyParams,
  type UpsertCompanyParams,
  type CompanyFilters,
  type CreateContactParams,
  type UpdateContactParams,
  type UpsertContactParams,
  type ContactFilters,
  type UpsertDealParams,
  type AcqDeal,
  type DealActivityEntry,
  type AddContactsToListParams,
  type AddContactsToListResult,
  type BulkImportParams,
  type BulkImportResult,
  type BulkImportCompanyEntry,
  type BulkImportCompaniesParams,
  type BulkImportCompaniesResult,
  type DeactivateContactsByCompanyParams,
  type DeactivateContactsByCompanyResult,
  type ILeadService,
  type UpdateDiscoveryDataParams,
  type UpdateProposalDataParams,
  type MarkProposalSentParams,
  type MarkProposalReviewedParams,
  type UpdateCloseLostReasonParams,
  type UpdateFeesParams,
  type CacheInstantlyThreadIdsParams,
  type TransitionItemParams,
  type SetContactNurtureParams,
  type CancelSchedulesAndHitlByEmailParams,
  type CancelHitlByDealIdParams,
  type ClearDealFieldsParams,
  type DeleteDealParams,
  type DealFilters,
  type DealStageSummary,
  type StaleDeal,
  type DealPipelineAnalytics,
  type DealAnalyticsParams,
  type UpsertSocialPostParams,
  type UpsertSocialPostsParams,
  type UpsertSocialPostsResult,
  type AcqDealNote,
  type CreateDealNoteParams,
  type ListDealNotesParams,
  type AcqDealTask,
  type AcqDealTaskKind,
  type CreateDealTaskParams,
  type ListDealTasksParams,
  type ListDealTasksDueParams,
  type CompleteDealTaskParams,
  type RecordDealActivityParams,
  type GetDealByIdParams,
  type GetContactByIdParams,
  type GetCompanyByIdParams,
  type UpdateListConfigParams,
  type UpdateCompanyStageParams,
  type UpdateContactStageParams,
  type ClearCompanyStagesParams,
  type ClearContactStagesParams,
  type ListPendingCompanyIdsParams,
  type ListPendingContactIdsParams,
  type AddCompaniesToListParams,
  type AddCompaniesToListResult,
  type RemoveCompaniesFromListParams,
  type RemoveCompaniesFromListResult,
  type RecordListExecutionParams,
  type ListExecutionSummary,
  type TransitionDealParams,
  type LoadDealParams
} from './lead-service-types'

/**
 * Tool services registry
 * Contains all services available to tools
 */
export interface ToolServices {
  commandQueueService: ICommandQueueService | null
  taskSchedulerService: ITaskSchedulerService | null
  notificationsService: INotificationsService | null
  projectsService?: IProjectsService | null
  crmService?: ICrmService | null
  emailService: IEmailService | null
  credentialsService: ICredentialsService | null
  integrationService: IIntegrationService | null
  leadService: ILeadService | null
  storageService: IStorageService | null
  /** PDF rendering service - optional for gradual adoption */
  pdfService?: IPdfService | null
}

/**
 * Global registry for tool services
 * Ensures services are initialized before use
 */
class ToolServicesRegistry {
  private services: ToolServices = {
    commandQueueService: null,
    taskSchedulerService: null,
    notificationsService: null,
    projectsService: null,
    crmService: null,
    emailService: null,
    credentialsService: null,
    integrationService: null,
    leadService: null,
    storageService: null,
    pdfService: null
  }
  private initialized = false

  /**
   * Initialize registry with services
   * @throws Error if already initialized (prevents double initialization)
   */
  initialize(services: ToolServices): void {
    if (this.initialized) {
      throw new Error('ToolServices already initialized')
    }
    this.services = services
    this.initialized = true
  }

  /**
   * Get registered services
   * @throws Error if not initialized (fail-fast)
   */
  get(): ToolServices {
    if (!this.initialized) {
      throw new Error('ToolServices not initialized. Call toolServicesRegistry.initialize() from apps/api/src/main.ts')
    }
    return this.services
  }

  /**
   * Reset registry (testing only)
   * Allows test isolation by resetting global state
   */
  reset(): void {
    this.initialized = false
    this.services = {
      commandQueueService: null,
      taskSchedulerService: null,
      notificationsService: null,
      projectsService: null,
      crmService: null,
      emailService: null,
      credentialsService: null,
      integrationService: null,
      leadService: null,
      storageService: null,
      pdfService: null
    }
  }
}

/**
 * Global singleton registry
 * Initialized once at server startup
 */
export const toolServicesRegistry = new ToolServicesRegistry()

/**
 * Get tool services
 * Convenience function for tools to access services
 *
 * @example
 * const { commandQueue } = getToolServices()
 * if (!commandQueue) throw new Error('CommandQueueService not initialized')
 * await commandQueue.createTask(params)
 */
export const getToolServices = (): ToolServices => toolServicesRegistry.get()
