import { computed, defineComponent, onMounted, ref, watch, inject } from 'vue'
import { useRouter } from 'vue-router'
import { LoadingElement } from './_ui/functional-loader'
import { DemarcheEtapeFondamentale, DemarcheId, DemarcheSlug, demarcheSlugValidator } from 'camino-common/src/demarche'
import { AsyncData, CaminoHttpError } from '@/api/client-rest'
import { User, isAdministration, isEntrepriseOrBureauDEtude, isSuper } from 'camino-common/src/roles'
import { capitalize } from 'camino-common/src/strings'
import { TitresTypes } from 'camino-common/src/static/titresTypes'
import { TitresTypesTypes } from 'camino-common/src/static/titresTypesTypes'
import { TabId } from './_common/dsfr-perimetre'
import { ApiClient, apiClient } from '@/api/api-client'
import { Domaine } from '@/components/_common/domaine'
import { DsfrButton, DsfrButtonIcon, DsfrLink } from './_ui/dsfr-button'
import { TitreStatut } from './_common/titre-statut'
import { caminoFiltres } from 'camino-common/src/filters'
import { ReferencesTypes } from 'camino-common/src/static/referencesTypes'
import { activitesColonneIdAnnee } from './activites'
import { CaminoDate, getCurrent } from 'camino-common/src/date'
import { Alert } from './_ui/alert'
import { TitreGet, getMostRecentValuePropFromEtapeFondamentaleValide } from 'camino-common/src/titres'
import { titreIdOrSlugValidator, TitreIdOrSlug, TitreId } from 'camino-common/src/validators/titres'
import { TitresLinkForm } from './titre/titres-link-form'
import { canReadTitreActivites } from 'camino-common/src/permissions/activites'
import { TitreTimeline } from './titre/titre-timeline'
import { CommuneId } from 'camino-common/src/static/communes'
import { canDeleteTitre, canEditTitre, canHaveActivites, canSeeTitreLastModifiedDate } from 'camino-common/src/permissions/titres'
import { TitreDemarche } from '@/components/titre/titre-demarche'
import { isNotNullNorUndefined, isNullOrUndefined, isNullOrUndefinedOrEmpty, onlyUnique } from 'camino-common/src/typescript-tools'
import { DsfrSeparator } from './_ui/dsfr-separator'
import { TitreAbonnerButton } from './titre/titre-abonner-button'
import { EditPopup } from './titre/edit-popup'
import { RemovePopup } from './titre/remove-popup'
import { ADMINISTRATION_TYPE_IDS, AdministrationId, Administrations } from 'camino-common/src/static/administrations'
import { getAdministrationsLocales } from 'camino-common/src/administrations'
import { getGestionnairesByTitreTypeId } from 'camino-common/src/static/administrationsTitresTypes'
import { DemarcheEditPopup } from './titre/demarche-edit-popup'
import { PhaseWithAlterations, phaseWithAlterations } from './titre/phase'
import { SecteursMaritimes } from 'camino-common/src/static/facades'
import { userKey, entreprisesKey } from '@/moi'
import { Entreprise } from 'camino-common/src/entreprise'
import { CaminoRouter } from '@/typings/vue-router'
import { ModifiedDate } from './_common/modified-date'
import { DemarcheMiseEnConcurrence } from './titre/demarche-mise-en-concurrence'
import { DemarchesConsentement } from './titre/demarche-consentement'
import { useState } from '@/utils/vue-tsx-utils'
import { isMachineWithConsentement, MachineInfo } from 'camino-common/src/machines'
import { InitialSort } from './_ui/table'
import { HTTP_STATUS } from 'camino-common/src/http'
import { fr } from '@codegouvfr/react-dsfr'
import { TitreVisibilite } from './_common/titre-visibilite'
import { CaminoError } from 'camino-common/src/zod-tools'

const activitesSort: InitialSort<string> = {
  colonne: activitesColonneIdAnnee,
  ordre: 'desc',
}

const getMachinesInfo = (titre: TitreGet): Record<DemarcheId, MachineInfo> | CaminoError<string> => {
  const result = titre.demarches.map(demarche => MachineInfo.withMachineId(titre.titre_type_id, demarche.demarche_type_id, demarche.id, demarche.machine_id))

  const error = result.find(machineInfo => !machineInfo.valid)
  if (isNotNullNorUndefined(error)) {
    return { message: error.error }
  } else {
    return result.reduce<Record<DemarcheId, MachineInfo>>((acc, machineInfo) => {
      if (machineInfo.valid) {
        acc[machineInfo.value.demarcheId] = machineInfo.value
      }

      return acc
    }, {})
  }
}

export const Titre = defineComponent(() => {
  const router = useRouter()

  const user = inject(userKey)
  const entreprises = inject(entreprisesKey, ref([]))

  const titreIdOrSlug = computed<TitreIdOrSlug | null>(() => {
    const currentRoute = router.currentRoute.value

    if (currentRoute.name === 'titre' && 'id' in currentRoute.params) {
      const idOrSlug = Array.isArray(currentRoute.params.id) ? currentRoute.params.id[0] : currentRoute.params.id
      const validated = titreIdOrSlugValidator.safeParse(idOrSlug)

      if (validated.success) {
        return validated.data
      }
    }

    return null
  })

  const currentDemarcheSlug = computed<DemarcheSlug | null>(() => {
    const demarcheId = router.currentRoute.value.query.demarcheSlug

    return (
      demarcheSlugValidator
        .nullable()
        .optional()
        .parse(Array.isArray(demarcheId) ? demarcheId[0] : demarcheId) ?? null
    )
  })

  return () => (
    <PureTitre
      user={user}
      entreprises={entreprises.value}
      titreIdOrSlug={titreIdOrSlug.value}
      currentDemarcheSlug={currentDemarcheSlug.value}
      apiClient={apiClient}
      router={router}
      currentDate={getCurrent()}
    />
  )
})

export interface Props {
  user: User
  entreprises: Entreprise[]
  titreIdOrSlug: TitreIdOrSlug | null
  currentDemarcheSlug: DemarcheSlug | null
  currentDate: CaminoDate
  apiClient: Pick<
    ApiClient,
    | 'getTitreById'
    | 'getTitresWithPerimetreForCarte'
    | 'deleteEtape'
    | 'deposeEtape'
    | 'loadTitreLinks'
    | 'loadLinkableTitres'
    | 'linkTitres'
    | 'titreUtilisateurAbonne'
    | 'getTitreUtilisateurAbonne'
    | 'editTitre'
    | 'removeTitre'
    | 'createDemarche'
    | 'updateDemarche'
    | 'deleteDemarche'
    | 'getDemarcheMiseEnConcurrence'
    | 'getEtapesTypesEtapesStatuts'
    | 'etapeCreer'
  >
  router: Pick<CaminoRouter, 'push' | 'replace'>
  initTab?: TabId
}

export const PureTitre = defineComponent<Props>(props => {
  const titreData = ref<AsyncData<{ titre: TitreGet; machinesInfo: Record<DemarcheId, MachineInfo> }>>({ status: 'LOADING' })
  const [hasTitresFrom, setHasTitresFrom] = useState<boolean | 'LOADING'>('LOADING')

  const retrieveTitre = async () => {
    const titreId: TitreId | null = titreData.value.status === 'LOADED' ? titreData.value.value.titre.id : null
    if (titreId !== null) {
      await updateTitre(titreId)
    }
  }

  const reloadAfterRemoveTitre = () => {
    props.router.push({ name: 'homepage', params: {} })
  }

  const demarcheCreatedOrUpdated = async (demarcheSlug: DemarcheSlug) => {
    if (props.titreIdOrSlug !== null) {
      await retrieveTitre()
      props.router.push({ name: 'titre', params: { id: props.titreIdOrSlug }, query: { demarcheSlug } })
    }
  }

  const demarcheDeleted = async () => {
    await retrieveTitre()
  }

  const updateTitre = async (titreIdOrSlug: TitreIdOrSlug | null) => {
    titreData.value = { status: 'LOADING' }

    if (titreIdOrSlug === null) {
      titreData.value = { status: 'ERROR', message: "Id ou slug d'activité non trouvé" }
    } else {
      try {
        const data = await props.apiClient.getTitreById(titreIdOrSlug)
        const machinesInfo = getMachinesInfo(data)

        if ('message' in machinesInfo) {
          titreData.value = { status: 'NEW_ERROR', error: machinesInfo }
        } else {
          titreData.value = { status: 'LOADED', value: { titre: data, machinesInfo } }

          let demarcheSlug = props.currentDemarcheSlug

          if (isNullOrUndefinedOrEmpty(demarcheSlug) && 'erreur' in phases.value) {
            demarcheSlug = phases.value.demarches[0].slug
          } else if (
            (isNullOrUndefinedOrEmpty(demarcheSlug) || isNullOrUndefined(data.demarches.find(({ slug }) => slug === props.currentDemarcheSlug))) &&
            !('erreur' in phases.value) &&
            phases.value.length > 0
          ) {
            demarcheSlug = phases.value[phases.value.length - 1][phases.value[phases.value.length - 1].length - 1].slug
          }

          if (data.slug !== props.titreIdOrSlug || demarcheSlug !== props.currentDemarcheSlug) {
            props.router.replace({ name: 'titre', params: { id: data.slug }, query: { demarcheSlug } })
          }
        }
      } catch (e: any) {
        console.error('error', e)
        if (e instanceof CaminoHttpError && e.statusCode === HTTP_STATUS.NOT_FOUND) {
          titreData.value = {
            status: 'NEW_ERROR',
            error: { message: 'Titre non trouvé', detail: isNullOrUndefined(props.user) ? 'Connectez vous et re-essayez' : "Soit le titre n'existe pas, soit vous n'avez pas les droits d'y accéder" },
          }
        } else {
          titreData.value = {
            status: 'ERROR',
            message: e.message ?? "Une erreur s'est produite",
          }
        }
      }
    }
  }

  watch(
    () => props.titreIdOrSlug,
    async (_newValue, _oldValue) => {
      if (titreData.value.status !== 'LOADED' || (titreData.value.value.titre.id !== props.titreIdOrSlug && titreData.value.value.titre.slug !== props.titreIdOrSlug)) {
        await updateTitre(props.titreIdOrSlug)
      }
    }
  )

  watch(
    () => titreData.value,
    async () => {
      if (titreData.value.status === 'LOADED') {
        const titre = titreData.value.value.titre

        showActivitesLink.value =
          canHaveActivites({
            titreTypeId: titreData.value.value.titre.titre_type_id,
            communes: communes.value,
            secteursMaritime: secteursMaritime.value,
            demarches: titreData.value.value.titre.demarches,
          }) &&
          (await canReadTitreActivites(
            props.user,
            () => Promise.resolve(titre.titre_type_id),
            () => Promise.resolve(administrations.value),
            () => {
              const titulaires = getMostRecentValuePropFromEtapeFondamentaleValide('titulaireIds', titre.demarches) ?? []
              const amodiataires = getMostRecentValuePropFromEtapeFondamentaleValide('amodiataireIds', titre.demarches) ?? []

              return Promise.resolve([...titulaires, ...amodiataires])
            }
          ))
      } else {
        showActivitesLink.value = false
      }
    }
  )

  onMounted(async () => {
    await updateTitre(props.titreIdOrSlug)
  })

  const customApiClient = computed<Props['apiClient']>(() => ({
    ...props.apiClient,
    deleteEtape: async titreEtapeId => {
      if (titreData.value.status === 'LOADED') {
        const result = await props.apiClient.deleteEtape(titreEtapeId)
        if (typeof result === 'object' && 'message' in result) {
          return result
        }

        await retrieveTitre()
      }
    },
    deposeEtape: async titreEtapeId => {
      if (titreData.value.status === 'LOADED') {
        const value = await props.apiClient.deposeEtape(titreEtapeId)
        if ('message' in value) {
          return value
        }
        await retrieveTitre()
        return value
      }
      return { message: "Le titre n'est pas chargé" as const }
    },
  }))

  const showActivitesLink = ref<boolean>(false)

  const perimetre = computed<null | DemarcheEtapeFondamentale['fondamentale']['perimetre']>(() => {
    if (titreData.value.status === 'LOADED' && titreData.value.value.titre.demarches !== null) {
      return getMostRecentValuePropFromEtapeFondamentaleValide('perimetre', titreData.value.value.titre.demarches)
    }

    return null
  })

  const administrations = computed<AdministrationId[]>(() => {
    if (titreData.value.status === 'LOADED') {
      const administrationLocales =
        perimetre.value !== null
          ? getAdministrationsLocales(
              perimetre.value.communes.map(({ id }) => id),
              perimetre.value.secteurs_maritimes
            )
          : []

      const administrationGestionnaires =
        titreData.value.value.titre.titre_type_id !== null
          ? getGestionnairesByTitreTypeId(titreData.value.value.titre.titre_type_id)
              .filter(({ associee }) => !associee)
              .filter(({ administrationId }) => Administrations[administrationId].typeId === ADMINISTRATION_TYPE_IDS.MINISTERE)
              .map(({ administrationId }) => administrationId)
          : []

      return [...administrationLocales, ...administrationGestionnaires].filter(onlyUnique)
    } else {
      return []
    }
  })

  const communes = computed<{ id: CommuneId }[]>(() => {
    if (perimetre.value !== null) {
      return perimetre.value.communes
    }

    return []
  })

  const secteursMaritime = computed<SecteursMaritimes[]>(() => {
    if (perimetre.value !== null) {
      return perimetre.value.secteurs_maritimes
    }

    return []
  })

  const titreLinkFormTitre = computed(() => {
    if (titreData.value.status === 'LOADED') {
      return { id: titreData.value.value.titre.id, typeId: titreData.value.value.titre.titre_type_id, administrations: administrations.value, demarches: titreData.value.value.titre.demarches }
    }

    return null
  })

  const phases = computed<PhaseWithAlterations>(() => {
    if (titreData.value.status === 'LOADED') {
      return phaseWithAlterations(titreData.value.value.titre.demarches, props.currentDate)
    }

    return []
  })

  const editerTitrePopup = ref<boolean>(false)
  const openEditerTitrePopup = () => {
    editerTitrePopup.value = true
  }

  const closeEditPopup = () => {
    editerTitrePopup.value = false
  }
  const supprimerTitrePopup = ref<boolean>(false)
  const openDeleteTitrePopup = () => {
    supprimerTitrePopup.value = true
  }

  const closeDeletePopup = () => {
    supprimerTitrePopup.value = false
  }

  const hasNoPhases = computed<boolean>(() => {
    return !('erreur' in phases.value) && phases.value.length === 0
  })

  const addDemarchePopup = ref<boolean>(false)

  const openAddDemarchePopup = () => {
    addDemarchePopup.value = true
  }
  const closeAddDemarchePopup = () => {
    addDemarchePopup.value = false
  }

  const machineId = computed(() => {
    if (titreData.value.status !== 'LOADED') {
      return undefined
    }
    if (isNullOrUndefined(props.currentDemarcheSlug)) {
      return undefined
    }
    const titre = titreData.value.value.titre
    const demarche = titre.demarches.find(({ slug }) => slug === props.currentDemarcheSlug)
    if (isNullOrUndefined(demarche)) {
      return undefined
    }
    return demarche.machine_id
  })

  return () => (
    <div>
      <LoadingElement
        data={titreData.value}
        renderItem={({ titre, machinesInfo }) => (
          <div>
            <div class="fr-grid-row fr-grid-row--top">
              <h1 style={{ maxWidth: '400px' }} class="fr-m-0">
                {capitalize(titre.nom)}
              </h1>
              <TitreStatut class="fr-ml-2w" titreStatutId={titre.titre_statut_id} style={{ alignSelf: 'center' }} />

              <div class="fr-m-0" style={{ marginLeft: 'auto !important', display: 'flex' }}>
                <DsfrLink
                  class="fr-btn fr-btn--secondary fr-pl-2w fr-pr-2w"
                  style={{ marginLeft: 'auto', display: 'flex' }}
                  disabled={false}
                  icon={null}
                  title="Signaler une erreur"
                  href={`mailto:camino@beta.gouv.fr?subject=Erreur ${titre.slug}&body=Bonjour, j'ai repéré une erreur sur le titre ${window.location.href} :`}
                  target="_blank"
                  rel="noopener external"
                />
                {canEditTitre(props.user, titre.titre_type_id, titre.titre_statut_id, administrations.value) ? (
                  <DsfrButtonIcon icon="fr-icon-pencil-line" buttonType="secondary" class="fr-ml-2w" title="Éditer le titre" onClick={openEditerTitrePopup} />
                ) : null}
                {canDeleteTitre(props.user) ? (
                  <DsfrButtonIcon icon="fr-icon-delete-bin-line" class="fr-ml-2w" title="Supprimer le titre" buttonType="secondary" onClick={openDeleteTitrePopup} />
                ) : null}
                <TitreAbonnerButton class="fr-ml-2w" titreId={titre.id} user={props.user} apiClient={props.apiClient} />
              </div>
            </div>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
              <div class={fr.cx('fr-grid-row', 'fr-grid-row--middle')}>
                <p class={fr.cx('fr-h3', 'fr-m-0')}>{capitalize(TitresTypesTypes[TitresTypes[titre.titre_type_id].typeId].nom)}</p>
                <Domaine class={fr.cx('fr-ml-2w')} domaineId={TitresTypes[titre.titre_type_id].domaineId} />
              </div>
              <TitreVisibilite titre_visibilite={titre.titre_visibilite} user={props.user} />
            </div>

            {titre.references.length > 0 ? (
              <div class="fr-mt-2w">
                Références
                {titre.references.map(reference => (
                  <div key={reference.nom} style={{ fontWeight: 500 }}>
                    {ReferencesTypes[reference.referenceTypeId].nom} : {reference.nom}
                  </div>
                ))}
              </div>
            ) : null}

            <div class="fr-grid-row fr-grid-row--middle fr-mt-4w">
              {titre.titre_last_modified_date !== null && canSeeTitreLastModifiedDate(props.user) ? <ModifiedDate modified_date={titre.titre_last_modified_date} /> : null}

              {isSuper(props.user) ? (
                <DsfrLink
                  style={{ marginLeft: 'auto' }}
                  disabled={false}
                  icon={null}
                  title="Journaux du titre"
                  label="Journaux du titre"
                  to={{ name: 'journaux', params: {}, query: { [caminoFiltres.titresIds.id]: titre.id } }}
                />
              ) : null}
            </div>
            <div>
              {titre.titre_doublon !== null ? (
                <Alert
                  small={true}
                  type="warning"
                  class="fr-mt-2w"
                  title={
                    <>
                      Le titre est en doublon avec : <DsfrLink disabled={false} icon={null} title={titre.titre_doublon?.nom ?? ''} to={{ name: 'titre', params: { id: titre.titre_doublon?.id } }} />.
                    </>
                  }
                />
              ) : null}
              {showActivitesLink.value && isNotNullNorUndefined(titre.nb_activites_to_do) ? (
                <>
                  {titre.nb_activites_to_do > 0 ? (
                    <Alert
                      class="fr-mt-2w"
                      small={true}
                      type={isEntrepriseOrBureauDEtude(props.user) ? 'warning' : 'info'}
                      title={
                        <>
                          Il manque {titre.nb_activites_to_do} {(titre.nb_activites_to_do ?? 0) > 1 ? "rapports d'activités" : "rapport d'activité"}.{' '}
                          <DsfrLink
                            disabled={false}
                            icon={null}
                            title={`Remplir ${(titre.nb_activites_to_do ?? 0) > 1 ? "les rapports d'activités" : "le rapport d'activité"}`}
                            to={{ name: 'activites', params: {}, query: { [caminoFiltres.titresIds.id]: titre.id, ...activitesSort } }}
                          />
                        </>
                      }
                    />
                  ) : null}
                  {titre.nb_activites_to_do === 0 || isAdministration(props.user) || isSuper(props.user) ? (
                    <DsfrLink
                      disabled={false}
                      icon={null}
                      class="fr-mt-2w"
                      title="Consulter les rapports d'activités"
                      label="Consulter les rapports d'activités"
                      buttonType="secondary"
                      to={{ name: 'activites', params: {}, query: { [caminoFiltres.titresIds.id]: titre.id, ...activitesSort } }}
                    />
                  ) : null}
                </>
              ) : null}
              {titreLinkFormTitre.value !== null ? <TitresLinkForm user={props.user} titre={titreLinkFormTitre.value} apiClient={props.apiClient} onTitresFromLoaded={setHasTitresFrom} /> : null}
            </div>

            <DemarcheMiseEnConcurrence apiClient={props.apiClient} titre={titre} user={props.user ?? null} hasTitresFrom={hasTitresFrom.value} />
            {isNotNullNorUndefined(props.currentDemarcheSlug) && isMachineWithConsentement(machineId.value) ? <DemarchesConsentement titre={titre} demarcheSlug={props.currentDemarcheSlug} /> : null}
            <DsfrSeparator />
            {hasNoPhases.value ? <DsfrButton style={{ marginLeft: 'auto' }} buttonType="primary" title="Ajouter une démarche" onClick={openAddDemarchePopup} /> : null}
            {props.currentDemarcheSlug !== null ? (
              <>
                <TitreTimeline class="fr-pt-4w fr-pb-4w" phasesWithAlterations={phases.value} currentDemarcheSlug={props.currentDemarcheSlug} titreSlug={titre.slug} />
                <TitreDemarche
                  titre={titre}
                  demarcheCreatedOrUpdated={demarcheCreatedOrUpdated}
                  demarcheDeleted={demarcheDeleted}
                  demarches={titre.demarches}
                  machinesInfo={machinesInfo}
                  currentDemarcheSlug={props.currentDemarcheSlug}
                  currentDate={props.currentDate}
                  user={props.user}
                  entreprises={props.entreprises}
                  apiClient={customApiClient.value}
                  router={props.router}
                  initTab={props.initTab}
                />
              </>
            ) : null}
            {editerTitrePopup.value ? <EditPopup close={closeEditPopup} apiClient={props.apiClient} titre={titre} reload={retrieveTitre} /> : null}
            {supprimerTitrePopup.value ? (
              <RemovePopup apiClient={props.apiClient} close={closeDeletePopup} titreId={titre.id} titreNom={titre.nom} titreTypeId={titre.titre_type_id} reload={reloadAfterRemoveTitre} />
            ) : null}
            {addDemarchePopup.value ? (
              <DemarcheEditPopup
                apiClient={props.apiClient}
                close={closeAddDemarchePopup}
                titreNom={titre.nom}
                titreTypeId={titre.titre_type_id}
                demarche={{ titreId: titre.id }}
                tabId="demarches"
                reload={demarcheCreatedOrUpdated}
              />
            ) : null}
          </div>
        )}
      />
    </div>
  )
})

// @ts-ignore waiting for https://github.com/vuejs/core/issues/7833
PureTitre.props = ['user', 'entreprises', 'titreIdOrSlug', 'apiClient', 'router', 'initTab', 'currentDemarcheSlug', 'currentDate']
