import { describe, expect, it } from 'vitest'
import {
  getAllBuildTemplates,
  getAllPipelines,
  getAllProjectStatuses,
  getAllProspectingStages,
  getLeadGenStageCatalog,
  getStagesInPipeline
} from '../migration-helpers'
import { resolveOrganizationModel } from '../resolve'
import type { DeepPartial, OrganizationModel } from '../types'

function resolveModel(partial: DeepPartial<OrganizationModel>): OrganizationModel {
  return resolveOrganizationModel(partial)
}

function modelWithPipelines(): OrganizationModel {
  return resolveModel({
    systems: {
      'sales.crm': {
        id: 'sales.crm',
        order: 70,
        label: 'CRM',
        lifecycle: 'active',
        path: '/crm',
        ontology: {
          objectTypes: {
            'sales.crm:object/deal': {
              id: 'sales.crm:object/deal',
              label: 'Deal',
              ownerSystemId: 'sales.crm'
            }
          },
          catalogTypes: {
            'sales.crm:catalog/default-pipeline': {
              id: 'sales.crm:catalog/default-pipeline',
              label: 'Default Deal Pipeline',
              kind: 'pipeline',
              appliesTo: 'sales.crm:object/deal',
              entries: {
                open: { label: 'Open', order: 10, semanticClass: 'open' },
                active: { label: 'Active', order: 20, semanticClass: 'active' },
                'closed-won': { label: 'Closed Won', order: 30, semanticClass: 'closed_won' }
              }
            }
          }
        }
      },
      projects: {
        id: 'projects',
        order: 90,
        label: 'Projects',
        lifecycle: 'active',
        path: '/projects',
        ontology: {
          objectTypes: {
            'projects:object/project': {
              id: 'projects:object/project',
              label: 'Project',
              ownerSystemId: 'projects'
            }
          },
          catalogTypes: {
            'projects:catalog/projects-pipeline': {
              id: 'projects:catalog/projects-pipeline',
              label: 'Projects Pipeline',
              kind: 'pipeline',
              appliesTo: 'projects:object/project',
              entries: {
                lead: { label: 'Lead', order: 10, semanticClass: 'open' }
              }
            }
          }
        }
      }
    }
  })
}

function modelWithBuildTemplate(): OrganizationModel {
  return resolveModel({
    systems: {
      'sales.lead-gen': {
        id: 'sales.lead-gen',
        order: 80,
        label: 'Lead Gen',
        lifecycle: 'active',
        path: '/lead-gen',
        ontology: {
          objectTypes: {
            'sales.lead-gen:object/list': {
              id: 'sales.lead-gen:object/list',
              label: 'Lead List',
              ownerSystemId: 'sales.lead-gen'
            }
          },
          catalogTypes: {
            'sales.lead-gen:catalog/build-template': {
              id: 'sales.lead-gen:catalog/build-template',
              label: 'Build Templates',
              kind: 'template',
              appliesTo: 'sales.lead-gen:object/list',
              entries: {
                'local-services': {
                  label: 'Local Services',
                  color: 'blue',
                  stepCatalog: 'sales.lead-gen:catalog/template-step.local-services'
                }
              }
            },
            'sales.lead-gen:catalog/template-step.local-services': {
              id: 'sales.lead-gen:catalog/template-step.local-services',
              label: 'Local Services Steps',
              kind: 'template-step',
              appliesTo: 'sales.lead-gen:object/list',
              entries: {
                'qualify-companies': {
                  label: 'Qualify Companies',
                  order: 20,
                  primaryEntity: 'company',
                  outputs: ['company'],
                  stageKey: 'qualified',
                  dependsOn: ['source-companies'],
                  dependencyMode: 'per-record-eligibility',
                  actionKey: 'lead-gen.company.qualify',
                  defaultBatchSize: 100,
                  maxBatchSize: 500
                },
                'source-companies': {
                  label: 'Source Companies',
                  order: 10,
                  primaryEntity: 'company',
                  outputs: ['company'],
                  stageKey: 'populated',
                  dependencyMode: 'per-record-eligibility',
                  actionKey: 'lead-gen.company.source',
                  defaultBatchSize: 100,
                  maxBatchSize: 500
                }
              }
            }
          }
        }
      }
    }
  })
}

function modelWithProspectingStages(): OrganizationModel {
  return resolveModel({
    systems: {
      'sales.lead-gen': {
        id: 'sales.lead-gen',
        order: 80,
        label: 'Lead Gen',
        lifecycle: 'active',
        path: '/lead-gen',
        ontology: {
          objectTypes: {
            'sales.lead-gen:object/company': {
              id: 'sales.lead-gen:object/company',
              label: 'Lead Company',
              ownerSystemId: 'sales.lead-gen'
            },
            'sales.lead-gen:object/contact': {
              id: 'sales.lead-gen:object/contact',
              label: 'Lead Contact',
              ownerSystemId: 'sales.lead-gen'
            }
          },
          catalogTypes: {
            'sales.lead-gen:catalog/company-stage': {
              id: 'sales.lead-gen:catalog/company-stage',
              label: 'Company Stages',
              kind: 'stage',
              appliesTo: 'sales.lead-gen:object/company',
              entries: {
                populated: { label: 'Populated', description: 'Companies imported.', order: 10 },
                qualified: { label: 'Qualified', description: 'Companies qualified.', order: 20 }
              }
            },
            'sales.lead-gen:catalog/contact-stage': {
              id: 'sales.lead-gen:catalog/contact-stage',
              label: 'Contact Stages',
              kind: 'stage',
              appliesTo: 'sales.lead-gen:object/contact',
              entries: {
                discovered: { label: 'Discovered', description: 'Contacts found.', order: 10 },
                uploaded: {
                  label: 'Uploaded',
                  description: 'Contacts exported.',
                  order: 40,
                  additionalEntities: ['company'],
                  recordEntity: 'company',
                  recordStageKey: 'uploaded'
                }
              }
            }
          }
        }
      }
    }
  })
}

function modelWithProjectStatuses(): OrganizationModel {
  return resolveModel({
    systems: {
      projects: {
        id: 'projects',
        order: 90,
        label: 'Projects',
        lifecycle: 'active',
        path: '/projects',
        ontology: {
          objectTypes: {
            'projects:object/project': { id: 'projects:object/project', label: 'Project', ownerSystemId: 'projects' },
            'projects:object/milestone': {
              id: 'projects:object/milestone',
              label: 'Milestone',
              ownerSystemId: 'projects'
            },
            'projects:object/task': { id: 'projects:object/task', label: 'Task', ownerSystemId: 'projects' }
          },
          catalogTypes: {
            'projects:catalog/project-status': {
              id: 'projects:catalog/project-status',
              label: 'Project Statuses',
              kind: 'status-flow',
              appliesTo: 'projects:object/project',
              entries: {
                active: { label: 'Active', order: 10 },
                completed: { label: 'Completed', order: 20 }
              }
            },
            'projects:catalog/milestone-status': {
              id: 'projects:catalog/milestone-status',
              label: 'Milestone Statuses',
              kind: 'status-flow',
              appliesTo: 'projects:object/milestone',
              entries: {
                upcoming: { label: 'Upcoming', order: 10 }
              }
            },
            'projects:catalog/task-status': {
              id: 'projects:catalog/task-status',
              label: 'Task Statuses',
              kind: 'status-flow',
              appliesTo: 'projects:object/task',
              entries: {
                planned: { label: 'Planned', order: 10 },
                'in-progress': { label: 'In Progress', order: 20 }
              }
            }
          }
        }
      }
    }
  })
}

describe('getAllPipelines', () => {
  it('returns all ontology-authored pipelines with owning system paths', () => {
    const result = getAllPipelines(modelWithPipelines())

    expect(result.map((entry) => [entry.systemPath, entry.pipeline.id])).toEqual([
      ['projects', 'projects-pipeline'],
      ['sales.crm', 'default-pipeline']
    ])
  })

  it('reconstructs pipeline stages from catalog entries sorted by order', () => {
    const pipeline = getAllPipelines(modelWithPipelines()).find((entry) => entry.pipeline.id === 'default-pipeline')

    expect(pipeline?.pipeline.entityId).toBe('deal')
    expect(pipeline?.pipeline.stages.map((stage) => stage.id)).toEqual(['open', 'active', 'closed-won'])
  })

  it('returns empty arrays when no ontology pipeline catalog exists', () => {
    expect(getAllPipelines(resolveOrganizationModel())).toEqual([])
  })
})

describe('getStagesInPipeline', () => {
  it('returns stages for a named ontology pipeline catalog', () => {
    expect(getStagesInPipeline(modelWithPipelines(), 'sales.crm', 'default-pipeline').map((stage) => stage.id)).toEqual([
      'open',
      'active',
      'closed-won'
    ])
  })

  it('returns empty arrays for unknown system paths and pipeline ids', () => {
    const model = modelWithPipelines()

    expect(getStagesInPipeline(model, 'missing', 'default-pipeline')).toEqual([])
    expect(getStagesInPipeline(model, 'sales.crm', 'missing')).toEqual([])
  })
})

describe('getAllBuildTemplates', () => {
  it('returns templates with steps from referenced ontology step catalogs', () => {
    const [template] = getAllBuildTemplates(modelWithBuildTemplate())

    expect(template).toMatchObject({ id: 'local-services', label: 'Local Services', color: 'blue' })
    expect(template?.steps.map((step) => step.id)).toEqual(['source-companies', 'qualify-companies'])
  })

  it('returns an empty array when no template catalog exists', () => {
    expect(getAllBuildTemplates(resolveOrganizationModel())).toEqual([])
  })
})

describe('getAllProspectingStages', () => {
  it('returns company and contact stages from ontology stage catalogs', () => {
    const model = modelWithProspectingStages()

    expect(getAllProspectingStages(model, 'company').map((stage) => stage.id)).toEqual(['populated', 'qualified'])
    expect(getAllProspectingStages(model, 'contact').map((stage) => stage.id)).toEqual(['discovered', 'uploaded'])
  })

  it('returns empty arrays when no matching ontology stage catalog exists', () => {
    const model = resolveOrganizationModel()

    expect(getAllProspectingStages(model, 'company')).toEqual([])
    expect(getAllProspectingStages(model, 'contact')).toEqual([])
  })
})

describe('getLeadGenStageCatalog', () => {
  it('returns the combined lead-gen stage catalog shape from ontology', () => {
    const result = getLeadGenStageCatalog(modelWithProspectingStages())

    expect(result.populated).toMatchObject({ key: 'populated', entity: 'company', order: 10 })
    expect(result.discovered).toMatchObject({ key: 'discovered', entity: 'contact', order: 10 })
    expect(result.uploaded).toMatchObject({
      key: 'uploaded',
      entity: 'contact',
      additionalEntities: ['company'],
      recordEntity: 'company',
      recordStageKey: 'uploaded'
    })
  })
})

describe('getAllProjectStatuses', () => {
  it('returns project, milestone, and task statuses from ontology status-flow catalogs', () => {
    const model = modelWithProjectStatuses()

    expect(getAllProjectStatuses(model, 'project').map((status) => status.id)).toEqual(['active', 'completed'])
    expect(getAllProjectStatuses(model, 'milestone').map((status) => status.id)).toEqual(['upcoming'])
    expect(getAllProjectStatuses(model, 'task').map((status) => status.id)).toEqual(['planned', 'in-progress'])
  })

  it('returns empty arrays when no matching ontology status-flow catalog exists', () => {
    const model = resolveOrganizationModel()

    expect(getAllProjectStatuses(model, 'project')).toEqual([])
    expect(getAllProjectStatuses(model, 'milestone')).toEqual([])
    expect(getAllProjectStatuses(model, 'task')).toEqual([])
  })
})
