import { titreEtapesSortAscByDate, titreEtapesSortAscByOrdre, titreEtapesSortDescByOrdre } from './titre-etapes-sort'
import { newDemarcheId, newEtapeId } from '../../database/models/_format/id-create'
import { vi, describe, test, expect } from 'vitest'
import { firstEtapeDateValidator, toCaminoDate } from 'camino-common/src/date'
import { DEMARCHES_TYPES_IDS } from 'camino-common/src/static/demarchesTypes'
import { TITRES_TYPES_IDS } from 'camino-common/src/static/titresTypes'
import { ETAPES_TYPES } from 'camino-common/src/static/etapesTypes'
import { TitreEtapeForMachine } from '../rules-demarches/machine-common'
import { ETAPE_IS_BROUILLON, ETAPE_IS_NOT_BROUILLON } from 'camino-common/src/etape'
import { MachineInfo } from 'camino-common/src/machines'
import { throwableMachineInfoForTestsOnly } from '../../../tests/_utils'

const titreEtapesSortedDescResult = [
  { typeId: ETAPES_TYPES.publicationDeDecisionAuJORF, ordre: 2, date: '1988-03-11' },
  { typeId: ETAPES_TYPES.decisionDeLAutoriteAdministrative, ordre: 1, date: '1988-03-06' },
] as TitreEtapeForMachine[]

const titreEtapesSortedAsc = [
  { typeId: ETAPES_TYPES.decisionDeLAutoriteAdministrative, ordre: 1, date: '1988-03-06' },
  { typeId: ETAPES_TYPES.publicationDeDecisionAuJORF, ordre: 2, date: '1988-03-11' },
] as TitreEtapeForMachine[]

const titreEtapesSortedDesc = titreEtapesSortedAsc.slice().reverse()

console.error = vi.fn()
console.warn = vi.fn()

describe('trie les étapes', () => {
  test('des étapes organisées par ordre décroissant sont triées par ordre croissant', () => {
    expect(titreEtapesSortAscByOrdre(titreEtapesSortedDesc)).toMatchObject(titreEtapesSortedAsc)
  })

  test('des étapes organisées par ordre croissant restent triées par ordre croissant', () => {
    expect(titreEtapesSortAscByOrdre(titreEtapesSortedAsc)).toMatchObject(titreEtapesSortedAsc)
  })

  test('des étapes organisées par ordre croissant sont triées par ordre décroissant', () => {
    expect(titreEtapesSortDescByOrdre(titreEtapesSortedAsc)).toMatchObject(titreEtapesSortedDescResult)
  })

  test('des étapes organisées par ordre décroissant restent triées par ordre décroissant', () => {
    expect(titreEtapesSortDescByOrdre(titreEtapesSortedDesc)).toMatchObject(titreEtapesSortedDescResult)
  })

  test('des étapes organisées par date décroissante sont triées par date croissante', () => {
    expect(
      titreEtapesSortAscByDate(titreEtapesSortedDesc, throwableMachineInfoForTestsOnly(TITRES_TYPES_IDS.PERMIS_EXCLUSIF_DE_RECHERCHES_HYDROCARBURE, DEMARCHES_TYPES_IDS.Octroi, newDemarcheId(), null))
    ).toMatchObject(titreEtapesSortedAsc)
  })

  test('des étapes organisées par date croissante restent triées par date croissante', () => {
    expect(
      titreEtapesSortAscByDate(titreEtapesSortedAsc, throwableMachineInfoForTestsOnly(TITRES_TYPES_IDS.PERMIS_EXCLUSIF_DE_RECHERCHES_HYDROCARBURE, DEMARCHES_TYPES_IDS.Octroi, newDemarcheId(), null))
    ).toMatchObject(titreEtapesSortedAsc)
  })

  test('des étapes avec les mêmes dates organisées par ordre décroissant sont triées par ordre croissant', () => {
    const titreEtapesMemesDatesOrdreDesc: TitreEtapeForMachine[] = [
      {
        id: newEtapeId('1'),
        typeId: ETAPES_TYPES.decisionDeLAutoriteAdministrative,
        ordre: 2,
        date: toCaminoDate('1988-03-06'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fai',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        communes: [],
        contenu: {},
        surface: null,
        demarcheIdsConsentement: [],
      },
      {
        id: newEtapeId('2'),
        typeId: ETAPES_TYPES.publicationDeDecisionAuJORF,
        ordre: 1,
        date: toCaminoDate('1988-03-06'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fav',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        communes: [],
        contenu: {},
        surface: null,
        demarcheIdsConsentement: [],
      },
    ]

    const titreEtapesMemesDatesOrdreAscResult = titreEtapesMemesDatesOrdreDesc.slice().reverse()

    expect(
      titreEtapesSortAscByDate(
        titreEtapesMemesDatesOrdreDesc,
        MachineInfo.withDate(TITRES_TYPES_IDS.AUTORISATION_DE_RECHERCHE_METAUX, DEMARCHES_TYPES_IDS.Octroi, newDemarcheId(), firstEtapeDateValidator.parse('1988-03-06'))
      )
    ).toStrictEqual(titreEtapesMemesDatesOrdreAscResult)
  })

  test('des étapes avec les mêmes dates sont triées par ordre de type croissant', () => {
    const titreDemarcheId = newDemarcheId('1')
    const titreEtapesMemesDatesOrdreEtapesTypesDesc: TitreEtapeForMachine[] = [
      {
        id: newEtapeId('1'),
        typeId: ETAPES_TYPES.saisineDuPrefet,
        ordre: 2,
        date: toCaminoDate('1988-03-06'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fav',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        communes: [],
        contenu: {},
        surface: null,
        demarcheIdsConsentement: [],
      },
      {
        id: newEtapeId('2'),
        typeId: ETAPES_TYPES.classementSansSuite,
        ordre: 2,
        date: toCaminoDate('1988-03-06'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fav',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        communes: [],
        contenu: {},
        surface: null,
        demarcheIdsConsentement: [],
      },
      {
        id: newEtapeId('3'),
        typeId: ETAPES_TYPES.decisionDeLAutoriteAdministrative,
        ordre: 2,
        date: toCaminoDate('1988-03-06'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fav',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        communes: [],
        contenu: {},
        surface: null,
        demarcheIdsConsentement: [],
      },
    ]
    expect(
      titreEtapesSortAscByDate(
        titreEtapesMemesDatesOrdreEtapesTypesDesc,
        MachineInfo.withDate(TITRES_TYPES_IDS.AUTORISATION_D_EXPLOITATION_METAUX, DEMARCHES_TYPES_IDS.Retrait, titreDemarcheId, firstEtapeDateValidator.parse('1988-03-06'))
      )
    ).toMatchObject([
      {
        typeId: ETAPES_TYPES.saisineDuPrefet,
        ordre: 2,
        date: '1988-03-06',
        statutId: 'fav',
      },
      {
        typeId: ETAPES_TYPES.decisionDeLAutoriteAdministrative,
        ordre: 2,
        date: '1988-03-06',
        statutId: 'fav',
      },
      {
        typeId: ETAPES_TYPES.classementSansSuite,
        ordre: 2,
        date: '1988-03-06',
        statutId: 'fav',
      },
    ])
  })

  test('tri selon l’arbre si les étapes ont la même date', () => {
    const etapes: TitreEtapeForMachine[] = [
      {
        id: newEtapeId('1'),
        typeId: ETAPES_TYPES.paiementDesFraisDeDossier,
        ordre: 1,
        date: toCaminoDate('2020-01-01'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fai',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        communes: [],
        contenu: {},
        surface: null,
        demarcheIdsConsentement: [],
      },
      {
        id: newEtapeId('2'),
        typeId: ETAPES_TYPES.demande,
        ordre: 2,
        date: toCaminoDate('2020-01-01'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fai',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        communes: [],
        contenu: {},
        surface: null,
        demarcheIdsConsentement: [],
      },
      {
        id: newEtapeId('3'),
        typeId: ETAPES_TYPES.enregistrementDeLaDemande,
        ordre: 3,
        date: toCaminoDate('2020-01-01'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fai',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        surface: null,
        communes: [],
        contenu: {},
        demarcheIdsConsentement: [],
      },
    ]

    const result = titreEtapesSortAscByDate(
      etapes,
      MachineInfo.withDate(TITRES_TYPES_IDS.AUTORISATION_DE_RECHERCHE_METAUX, DEMARCHES_TYPES_IDS.Octroi, newDemarcheId(), firstEtapeDateValidator.parse('2020-01-01'))
    )
    expect(result[0].typeId).toEqual('pfd')
    expect(result[1].typeId).toEqual('mfr')
    expect(result[2].typeId).toEqual('men')
  })

  test("retourne une erreur si le type d'étape est absent dans la définition", () => {
    const etapes: TitreEtapeForMachine[] = [
      {
        id: newEtapeId('1'),
        typeId: ETAPES_TYPES.recevabiliteDeLaDemande,
        date: toCaminoDate('2020-01-01'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fai',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        surface: null,
        ordre: 1,
        communes: [],
        contenu: null,
        demarcheIdsConsentement: [],
      },
      {
        // @ts-ignore
        typeId: 'bof',
        id: newEtapeId('2'),
        date: toCaminoDate('2020-01-01'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fai',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        surface: null,
        ordre: 2,
        communes: [],
        contenu: null,
      },
      {
        id: newEtapeId('3'),
        typeId: ETAPES_TYPES.validationDuPaiementDesFraisDeDossier,
        date: toCaminoDate('2020-01-01'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fai',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        surface: null,
        ordre: 3,
        communes: [],
        contenu: null,
        demarcheIdsConsentement: [],
      },
    ]

    expect(() =>
      titreEtapesSortAscByDate(
        etapes,
        MachineInfo.withDate(TITRES_TYPES_IDS.AUTORISATION_DE_RECHERCHE_METAUX, DEMARCHES_TYPES_IDS.Octroi, newDemarcheId(), firstEtapeDateValidator.parse('2020-01-01'))
      )
    ).toThrowErrorMatchingInlineSnapshot(`[Error: l'état bof est inconnu]`)
  })

  test('utilise l’id pour trier des étapes totalement identiques', () => {
    const secondMcd: TitreEtapeForMachine = {
      id: newEtapeId('mcd2'),
      typeId: ETAPES_TYPES.demandeDeComplements_DecisionDeLaMissionAutoriteEnvironnementale_ExamenAuCasParCasDuProjet_,
      date: toCaminoDate('2020-01-01'),
      dateFin: null,
      dateDebut: null,
      duree: null,
      statutId: 'fai',
      concurrence: 'non-applicable',
      hasTitreFrom: 'non-applicable',
      isBrouillon: ETAPE_IS_NOT_BROUILLON,
      surface: null,
      ordre: 5,
      contenu: {},
      communes: [],
      demarcheIdsConsentement: [],
    }

    const etapes: TitreEtapeForMachine[] = [
      {
        id: newEtapeId('mfr'),
        ordre: 1,
        typeId: ETAPES_TYPES.demande,
        date: toCaminoDate('2020-01-01'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fai',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        surface: null,
        contenu: {},
        demarcheIdsConsentement: [],
        communes: [],
      },
      {
        id: newEtapeId('men'),
        ordre: 2,
        typeId: ETAPES_TYPES.enregistrementDeLaDemande,
        date: toCaminoDate('2020-01-01'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fai',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        surface: null,
        contenu: {},
        communes: [],
        demarcheIdsConsentement: [],
      },
      {
        id: newEtapeId('mcd'),
        ordre: 3,
        typeId: ETAPES_TYPES.demandeDeComplements_DecisionDeLaMissionAutoriteEnvironnementale_ExamenAuCasParCasDuProjet_,
        date: toCaminoDate('2020-01-01'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fai',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        surface: null,
        contenu: {},
        communes: [],
        demarcheIdsConsentement: [],
      },
      {
        id: newEtapeId('rcd'),
        ordre: 4,
        typeId: ETAPES_TYPES.receptionDeComplements_DecisionDeLaMissionAutoriteEnvironnementale_ExamenAuCasParCasDuProjet__,
        date: toCaminoDate('2020-01-01'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fai',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        surface: null,
        contenu: {},
        communes: [],
        demarcheIdsConsentement: [],
      },
      secondMcd,
    ]

    const result = titreEtapesSortAscByDate(
      etapes,
      MachineInfo.withDate(TITRES_TYPES_IDS.AUTORISATION_DE_RECHERCHE_METAUX, DEMARCHES_TYPES_IDS.Octroi, newDemarcheId(), firstEtapeDateValidator.parse('2020-01-01'))
    )
    expect(result).toContain(secondMcd)
  })

  test('tri les étapes via la machine, et remet les étapes en brouillon en fonction de leur date', () => {
    const etapes: TitreEtapeForMachine[] = [
      {
        id: newEtapeId('mfr'),
        ordre: 1,
        typeId: ETAPES_TYPES.demande,
        date: toCaminoDate('2020-01-01'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fai',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        surface: null,
        contenu: {},
        communes: [],
        demarcheIdsConsentement: [],
      },
      {
        id: newEtapeId('men'),
        ordre: 2,
        typeId: ETAPES_TYPES.enregistrementDeLaDemande,
        date: toCaminoDate('2020-01-02'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fai',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        surface: null,
        contenu: {},
        communes: [],
        demarcheIdsConsentement: [],
      },
      {
        id: newEtapeId('pfd'),
        ordre: 3,
        typeId: ETAPES_TYPES.paiementDesFraisDeDossier,
        date: toCaminoDate('2020-01-03'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fai',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        surface: null,
        contenu: {},
        communes: [],
        demarcheIdsConsentement: [],
      },
      {
        id: newEtapeId('mcp'),
        ordre: 4,
        typeId: ETAPES_TYPES.completudeDeLaDemande,
        date: toCaminoDate('2020-01-04'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fav',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        surface: null,
        contenu: {},
        communes: [],
        demarcheIdsConsentement: [],
      },
      {
        id: newEtapeId('vfd'),
        ordre: 5,
        typeId: ETAPES_TYPES.validationDuPaiementDesFraisDeDossier,
        date: toCaminoDate('2020-01-05'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fai',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        surface: null,
        contenu: {},
        communes: [],
        demarcheIdsConsentement: [],
      },
      {
        id: newEtapeId('asc'),
        ordre: 0,
        typeId: ETAPES_TYPES.avisDesServicesEtCommissionsConsultatives,
        date: toCaminoDate('2020-01-07'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fai',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_BROUILLON,
        surface: null,
        contenu: {},
        communes: [],
        demarcheIdsConsentement: [],
      },
      {
        id: newEtapeId('mcr'),
        ordre: 6,
        typeId: ETAPES_TYPES.paiementDesFraisDeDossier,
        date: toCaminoDate('2020-01-06'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fai',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        surface: null,
        contenu: {},
        communes: [],
        demarcheIdsConsentement: [],
      },
    ]

    const result = titreEtapesSortAscByDate(etapes, throwableMachineInfoForTestsOnly(TITRES_TYPES_IDS.AUTORISATION_DE_RECHERCHE_METAUX, DEMARCHES_TYPES_IDS.Octroi, newDemarcheId(), null))
    expect(result.map(({ id }) => id)).toMatchInlineSnapshot(`
      [
        "mfr",
        "men",
        "pfd",
        "mcp",
        "vfd",
        "mcr",
        "asc",
      ]
    `)
    expect(result[result.length - 1].id).toBe('asc')
  })

  test("une demande en brouillon à la même date qu'une décision de l'administration est en 1ère position", () => {
    const etapes: TitreEtapeForMachine[] = [
      {
        id: newEtapeId('mfr'),
        typeId: ETAPES_TYPES.demande,
        date: toCaminoDate('2020-01-01'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fai',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_BROUILLON,
        surface: null,
        ordre: 1,
        communes: [],
        contenu: null,
        demarcheIdsConsentement: [],
      },
      {
        id: newEtapeId('dex'),
        typeId: ETAPES_TYPES.decisionDeLAutoriteAdministrative,
        date: toCaminoDate('2020-01-01'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'acc',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        surface: null,
        ordre: 3,
        communes: [],
        contenu: null,
        demarcheIdsConsentement: [],
      },
    ]
    const result = titreEtapesSortAscByDate(
      etapes,
      throwableMachineInfoForTestsOnly(TITRES_TYPES_IDS.PERMIS_D_EXPLOITATION_GEOTHERMIE, DEMARCHES_TYPES_IDS.Octroi, newDemarcheId(), 'ProcedureSimplifiee')
    )

    expect(result.map(({ id }) => id)).toMatchInlineSnapshot(`
         [
           "mfr",
           "dex",
         ]
       `)
  })

  test("une demande en brouillon à une date postérieure qu'une décision de l'administration est en 2ème position", () => {
    const etapes: TitreEtapeForMachine[] = [
      {
        id: newEtapeId('mfr'),
        typeId: ETAPES_TYPES.demande,
        date: toCaminoDate('2020-01-02'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'fai',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_BROUILLON,
        surface: null,
        ordre: 1,
        communes: [],
        contenu: null,
        demarcheIdsConsentement: [],
      },
      {
        id: newEtapeId('dex'),
        typeId: ETAPES_TYPES.decisionDeLAutoriteAdministrative,
        date: toCaminoDate('2020-01-01'),
        dateFin: null,
        dateDebut: null,
        duree: null,
        statutId: 'acc',
        concurrence: 'non-applicable',
        hasTitreFrom: 'non-applicable',
        isBrouillon: ETAPE_IS_NOT_BROUILLON,
        surface: null,
        ordre: 3,
        communes: [],
        contenu: null,
        demarcheIdsConsentement: [],
      },
    ]
    const result = titreEtapesSortAscByDate(
      etapes,
      throwableMachineInfoForTestsOnly(TITRES_TYPES_IDS.PERMIS_D_EXPLOITATION_GEOTHERMIE, DEMARCHES_TYPES_IDS.Octroi, newDemarcheId(), 'ProcedureSimplifiee')
    )

    expect(result.map(({ id }) => id)).toMatchInlineSnapshot(`
           [
             "dex",
             "mfr",
           ]
         `)
  })
})
