import { toCaminoDate, CaminoDate, FirstEtapeDate } from './date'
import { DemarcheId, demarcheIdValidator } from './demarche'
import { DemarcheTypeId, DEMARCHES_TYPES_IDS, DemarchesTypes } from './static/demarchesTypes'
import { TitreTypeId } from './static/titresTypes'
import { NonEmptyArray, isNullOrUndefinedOrEmpty, isNotNullNorUndefined, isNotNullNorUndefinedNorEmpty, toSorted, isNullOrUndefined } from './typescript-tools'
import { CaminoMachineId } from './validators/machine'

// TODO 2025-01-29 attention le jour où on met à jour la date de début de la procédure spécifique
// TODO 2025-01-29 pour le consentement, il faudrait que l'on calcule le consentement pour toutes les étapes avec un périmètre, et qu'on aille chercher le consentement lié au titre.props_etape_id
export const DATE_DEBUT_PROCEDURE_SPECIFIQUE = toCaminoDate('2400-01-01')
export const DATE_DEBUT_PROCEDURE_SPECIFIQUE_AXM_ARM = toCaminoDate('2024-07-01')
export const DATE_DEBUT_PRM_OCT = toCaminoDate('2019-10-31')

interface DemarcheDefinition {
  titreTypeIds: NonEmptyArray<TitreTypeId>
  demarcheTypeIds: DemarcheTypeId[]
  dateDebut: CaminoDate
  demarcheIdExceptions: DemarcheId[]
  machineId: CaminoMachineId
}
const allDemarcheNotTravaux = Object.values(DEMARCHES_TYPES_IDS).filter(demarcheTypeId => !DemarchesTypes[demarcheTypeId].travaux)
const demarcheTypeIdsCxPr_G: DemarcheTypeId[] = [
  DEMARCHES_TYPES_IDS.Mutation,
  DEMARCHES_TYPES_IDS.Amodiation,
  DEMARCHES_TYPES_IDS.Cession,
  DEMARCHES_TYPES_IDS.Conversion,
  DEMARCHES_TYPES_IDS.Decheance,
  DEMARCHES_TYPES_IDS.DemandeDeTitreDExploitation,
  DEMARCHES_TYPES_IDS.Fusion,
  DEMARCHES_TYPES_IDS.MutationPartielle,
  DEMARCHES_TYPES_IDS.RenonciationTotale,
  DEMARCHES_TYPES_IDS.RenonciationPartielle,
  DEMARCHES_TYPES_IDS.ResiliationAnticipeeDAmodiation,
  DEMARCHES_TYPES_IDS.Retrait,
]
const plusVieilleDateEnBase = toCaminoDate('1717-01-09')

export const demarchesDefinitions = [
  {
    titreTypeIds: ['arm'],
    demarcheTypeIds: ['oct'],
    dateDebut: toCaminoDate('2019-10-31'),
    demarcheIdExceptions: [demarcheIdValidator.parse('QauHUIPuv1a5neW3JqVEWh8n'), demarcheIdValidator.parse('XrNwRcLK39y6JZKcsJTV7nju')],
    machineId: 'AncienLogigrammeOctroiARM',
  },
  {
    titreTypeIds: ['arm'],
    demarcheTypeIds: [DEMARCHES_TYPES_IDS.RenonciationTotale, DEMARCHES_TYPES_IDS.RenonciationPartielle, DEMARCHES_TYPES_IDS.Prolongation],
    dateDebut: toCaminoDate('2019-10-31'),
    demarcheIdExceptions: [],
    machineId: 'AncienLogigrammeRenonciationEtProlongationARM',
  },
  {
    titreTypeIds: ['prm'],
    demarcheTypeIds: ['oct'],
    dateDebut: DATE_DEBUT_PRM_OCT,
    demarcheIdExceptions: [
      demarcheIdValidator.parse('FfJTtP9EEfvf3VZy81hpF7ms'),
      demarcheIdValidator.parse('lynG9hx3x05LaqpySr0qxeca'),
      demarcheIdValidator.parse('xjvFNG3I8YOv2xLw6FQJjTab'),
      demarcheIdValidator.parse('fWlR3sADjURm21wM2j7UZF3R'),
      demarcheIdValidator.parse('eySDrrpK4KKukIw3II3nk3G1'),
      demarcheIdValidator.parse('PYrSWWMeDDDYfJfgWa09LVlp'),
      demarcheIdValidator.parse('c7356e106f1bd5cfdffc0393'),
      demarcheIdValidator.parse('3ed1f39cca579d553f1de15a'),
      demarcheIdValidator.parse('ef2c98a459e4ec3f69b71f05'),
      demarcheIdValidator.parse('81c5b6245d61924949e00e60'),
      demarcheIdValidator.parse('14f2d962fe22e0ff1e8edf8d'),
      demarcheIdValidator.parse('7516487c18df23623908e116'),
      demarcheIdValidator.parse('a8d794b0981581ea5b7c753c'),
      demarcheIdValidator.parse('DGxGTYlnDDGamQtreDOhFlLl'),
      demarcheIdValidator.parse('2b47c4d7679f728a8a813063'),
      demarcheIdValidator.parse('pHJIIJFZpgEccJNt6zTOegzh'),
      demarcheIdValidator.parse('61af4c6a62b8e477e4b3f43a'),
    ],
    machineId: 'AncienLogigrammeOctroiPRM',
  },
  {
    titreTypeIds: ['axm'],
    demarcheTypeIds: ['oct'],
    // https://camino.beta.gouv.fr/titres/m-ax-crique-tumuc-humac-2020
    dateDebut: toCaminoDate('2020-10-01'),
    demarcheIdExceptions: [
      demarcheIdValidator.parse('C3rs92l1eci3mLvsAGkv7gVV'),
      demarcheIdValidator.parse('YEWeODXiFb7xKJB2OQlTyc14'),
      // avis dgtm moins de 30 jours après la saisine des services
      demarcheIdValidator.parse('ktPyoaDYzJi2faPMtAeKFZ5l'),
      // pas d'enregistrement de la demande
      demarcheIdValidator.parse('xbKfuhPmphDd68x4QqtyRctj'),
      demarcheIdValidator.parse('TUI2aBCJRv7Fc1torDlVu8HB'),
      demarcheIdValidator.parse('FPcbrdR79tjerabHbxtLkMZa'),
    ],
    machineId: 'AncienLogigrammeOctroiAXM',
  },
  {
    titreTypeIds: ['axm'],
    demarcheTypeIds: ['pro'],
    dateDebut: toCaminoDate('2000-01-01'),
    demarcheIdExceptions: [
      // Complète mais ne respectant pas le cacoo
      demarcheIdValidator.parse('Fq6lCWTS6h8k5dAsG6LLm3Gw'),
      demarcheIdValidator.parse('TlqKNgdYzYVrUXieMJAqWYBD'),
      demarcheIdValidator.parse('ka8jUJq3ESxAdhE6QacBlqS8'),
      demarcheIdValidator.parse('d2443R01mLB8Nv2ZAhNSZdx3'),
      demarcheIdValidator.parse('Od6oeREEAXvUyvdQWUOgKhTS'),
      demarcheIdValidator.parse('M7VhIeD27VR0kKrkPTQHyXeH'),
      demarcheIdValidator.parse('VWBvpOx3n4dN7WCQoYUEC6vM'),
      demarcheIdValidator.parse('A17SapPN5NzwSBEOeoagQcHt'),
      demarcheIdValidator.parse('tuQqpnDSYhnTGlkkxTsDUf0r'),
      demarcheIdValidator.parse('s8ONjdmsJivnfnhE4ENNVmOb'),
      demarcheIdValidator.parse('nk5alZi7lSkxGVMlmsmyaqOv'),
      demarcheIdValidator.parse('Eg3T3fvnJETbYBmd8BJYfc1h'),
      demarcheIdValidator.parse('ohHg9uU2zd9m3MvF6yJ3KxLr'),
      demarcheIdValidator.parse('zsDao5HywdHx7YRlWjEMklZJ'),
      demarcheIdValidator.parse('51G6AmHKKX5wFjbN6zJ3kufK'),
      demarcheIdValidator.parse('08eC26bUf4PCr6qj9Rl4Qa1F'),
      demarcheIdValidator.parse('OBKZ23yRO6e4VP7MyXwgCp6U'),
    ],
    machineId: 'AncienLogigrammeProlongationAXM',
  },
  {
    titreTypeIds: ['pxg', 'arg', 'cxr', 'inr', 'prr', 'pxr', 'cxf', 'prf', 'pxf', 'arc', 'apc', 'pcc'],
    demarcheTypeIds: allDemarcheNotTravaux,
    dateDebut: plusVieilleDateEnBase,
    demarcheIdExceptions: [],
    machineId: 'ProcedureSimplifiee',
  },
  {
    titreTypeIds: ['cxg', 'prg', 'cxs', 'prs', 'aph', 'cxh', 'prh', 'pxh'],
    demarcheTypeIds: demarcheTypeIdsCxPr_G,
    dateDebut: plusVieilleDateEnBase,
    demarcheIdExceptions: [],
    machineId: 'ProcedureSimplifiee',
  },
  {
    titreTypeIds: ['arc', 'cxg', 'cxw', 'cxh', 'cxm', 'cxs', 'pcc', 'prg', 'prw', 'prh', 'prm', 'prs'],
    demarcheTypeIds: [
      DEMARCHES_TYPES_IDS.Octroi,
      DEMARCHES_TYPES_IDS.Prolongation,
      DEMARCHES_TYPES_IDS.Prolongation1,
      DEMARCHES_TYPES_IDS.Prolongation2,
      DEMARCHES_TYPES_IDS.ProlongationExceptionnelle,
      DEMARCHES_TYPES_IDS.ExtensionDePerimetre,
      DEMARCHES_TYPES_IDS.ExtensionDeSubstance,
      DEMARCHES_TYPES_IDS.Prorogation,
    ],
    dateDebut: DATE_DEBUT_PROCEDURE_SPECIFIQUE,
    demarcheIdExceptions: [],
    machineId: 'ProcedureSpecifique',
  },
  {
    titreTypeIds: ['arm', 'axm'],
    demarcheTypeIds: [
      DEMARCHES_TYPES_IDS.Octroi,
      DEMARCHES_TYPES_IDS.Prolongation,
      DEMARCHES_TYPES_IDS.Prolongation1,
      DEMARCHES_TYPES_IDS.Prolongation2,
      DEMARCHES_TYPES_IDS.ProlongationExceptionnelle,
      DEMARCHES_TYPES_IDS.ExtensionDePerimetre,
      DEMARCHES_TYPES_IDS.ExtensionDeSubstance,
      DEMARCHES_TYPES_IDS.Prorogation,
    ],
    dateDebut: DATE_DEBUT_PROCEDURE_SPECIFIQUE_AXM_ARM,
    demarcheIdExceptions: [demarcheIdValidator.parse('demarcheIdForUnitTests')],
    machineId: 'ProcedureSpecifique',
  },
] as const satisfies readonly DemarcheDefinition[]

export const isDemarcheIdException = (id: DemarcheId): boolean => {
  return demarchesDefinitions.some(definitions => isNotNullNorUndefinedNorEmpty(definitions.demarcheIdExceptions) && definitions.demarcheIdExceptions.includes(id))
}

export class MachineInfo {
  private _machineId: CaminoMachineId | undefined
  private _titreTypeId: TitreTypeId
  private _demarcheTypeId: DemarcheTypeId
  private _demarcheId: DemarcheId

  private constructor(
    titreTypeId: TitreTypeId,
    demarcheTypeId: DemarcheTypeId,
    demarcheId: DemarcheId,
    dateOrMachineId: { type: 'date'; value: FirstEtapeDate } | { type: 'machine'; value: CaminoMachineId | null }
  ) {
    if (dateOrMachineId.type === 'machine') {
      this._machineId = dateOrMachineId.value ?? undefined
    } else {
      this._machineId = MachineInfo.machineIdFind(titreTypeId, demarcheTypeId, demarcheId, dateOrMachineId.value)
    }

    this._titreTypeId = titreTypeId
    this._demarcheTypeId = demarcheTypeId
    this._demarcheId = demarcheId
  }

  static withDate(titreTypeId: TitreTypeId, demarcheTypeId: DemarcheTypeId, demarcheId: DemarcheId, date: FirstEtapeDate): MachineInfo {
    return new MachineInfo(titreTypeId, demarcheTypeId, demarcheId, { type: 'date', value: date })
  }

  static withMachineId(
    titreTypeId: TitreTypeId,
    demarcheTypeId: DemarcheTypeId,
    demarcheId: DemarcheId,
    machineId: CaminoMachineId | null
  ): { valid: true; value: MachineInfo } | { valid: false; error: string } {
    const demarcheDefinitionMatching = demarchesDefinitions.filter(
      value =>
        value.titreTypeIds.includes(titreTypeId) &&
        value.demarcheTypeIds.includes(demarcheTypeId) &&
        (!isNotNullNorUndefinedNorEmpty(value.demarcheIdExceptions) || !value.demarcheIdExceptions.includes(demarcheId))
    )

    const logigrammesPotentiels = demarcheDefinitionMatching.map(({ machineId }) => machineId).join(', ')
    let found = false
    for (const value of demarcheDefinitionMatching) {
      if (isNullOrUndefined(machineId) && value.dateDebut === plusVieilleDateEnBase) {
        return { valid: false, error: `la machine n'est pas renseignée alors qu'il existe un logigramme potentiel : ${logigrammesPotentiels}` }
      }
      if (value.machineId === machineId) {
        found = true
      }
    }
    if (isNotNullNorUndefined(machineId) && !found) {
      return { valid: false, error: `INCONSISTENCE, aurait du être ${logigrammesPotentiels} mais est ${machineId}` }
    }

    return { valid: true, value: new MachineInfo(titreTypeId, demarcheTypeId, demarcheId, { type: 'machine', value: machineId }) }
  }

  get machineId(): CaminoMachineId | undefined {
    return this._machineId
  }
  get titreTypeId(): TitreTypeId {
    return this._titreTypeId
  }
  get demarcheTypeId(): DemarcheTypeId {
    return this._demarcheTypeId
  }
  get demarcheId(): DemarcheId {
    return this._demarcheId
  }

  private static machineIdFind(titreTypeId: TitreTypeId, demarcheTypeId: DemarcheTypeId, demarcheId: DemarcheId, date: FirstEtapeDate | null): CaminoMachineId | undefined {
    const definitions = toSorted(demarchesDefinitions, (a, b) => b.dateDebut.localeCompare(a.dateDebut)).filter(
      d => (isNullOrUndefinedOrEmpty(date) || d.dateDebut <= date) && d.titreTypeIds.includes(titreTypeId) && d.demarcheTypeIds.includes(demarcheTypeId)
    )

    return definitions.find(
      definition =>
        isNullOrUndefinedOrEmpty(definition.demarcheIdExceptions) || (isNotNullNorUndefinedNorEmpty(definition.demarcheIdExceptions) && !definition.demarcheIdExceptions.includes(demarcheId))
    )?.machineId
  }
}

export const isProcedureOuverte = (machineId: CaminoMachineId | undefined): boolean => machineId === undefined

export const isMachineWithConsentement = (machineId: CaminoMachineId | undefined | null): boolean => machineId === 'ProcedureSpecifique'
