import { EtapeAvis, EtapeAvisModification, EtapeId, TempEtapeAvis } from 'camino-common/src/etape'
import { DemarcheTypeId } from 'camino-common/src/static/demarchesTypes'
import { EtapeTypeId } from 'camino-common/src/static/etapesTypes'
import { TitreTypeId } from 'camino-common/src/static/titresTypes'
import { ApiClient } from '../../api/api-client'
import { FunctionalComponent, computed, defineComponent, onMounted, ref, watch } from 'vue'
import { isNonEmptyArray, isNotNullNorUndefined, isNotNullNorUndefinedNorEmpty, isNullOrUndefined, NonEmptyArray } from 'camino-common/src/typescript-tools'
import { LoadingElement } from '../_ui/functional-loader'
import { AsyncData } from '../../api/client-rest'
import { DsfrButtonIcon } from '../_ui/dsfr-button'
import { AddEtapeAvisPopup } from './add-etape-avis-popup'
import { dateFormat, FirstEtapeDate } from 'camino-common/src/date'
import { AvisStatut } from '../_common/etape-statut'
import { AvisTypeId, getAvisNom } from 'camino-common/src/static/avisTypes'
import { CommuneId } from 'camino-common/src/static/communes'
import { getAvisVisibilityLabel } from './etape-avis'
import { User } from 'camino-common/src/roles'
import { DemarcheId } from 'camino-common/src/demarche'
import { FlattenedContenu } from 'camino-common/src/etape-form'
import { capitalize } from 'camino-common/src/strings'
import { fr } from '@codegouvfr/react-dsfr'
import { Column, TableSimple } from '../_ui/table-simple'
import { TableRow } from '../_ui/table'
import { getAvisTypes } from 'camino-common/src/avisTypes'
import { isArmMecanise } from 'camino-common/src/static/mecanise'

interface Props {
  tde: {
    titreTypeId: TitreTypeId
    demarcheTypeId: DemarcheTypeId
    etapeTypeId: EtapeTypeId
    demarcheId: DemarcheId
    firstEtapeDate: FirstEtapeDate
  }
  onChange: (etapeAvis: (EtapeAvis | TempEtapeAvis)[]) => void
  etapeId: EtapeId | null
  contenu: FlattenedContenu
  communeIds: CommuneId[]
  apiClient: Pick<ApiClient, 'uploadTempDocument' | 'getEtapeAvisByEtapeId'>
  user: User
}

type WithIndex = { index: number }

type EtapeAvisModificationWithIndex = EtapeAvisModification & WithIndex
export const EtapeAvisEdit = defineComponent<Props>(props => {
  const etapeAvis = ref<AsyncData<EtapeAvis[]>>({ status: 'LOADING' })

  onMounted(async () => {
    if (isNotNullNorUndefined(props.etapeId)) {
      etapeAvis.value = { status: 'LOADING' }
      try {
        const result = await props.apiClient.getEtapeAvisByEtapeId(props.etapeId)

        etapeAvis.value = { status: 'LOADED', value: result }
      } catch (e: any) {
        console.error('error', e)
        etapeAvis.value = {
          status: 'ERROR',
          message: e.message ?? "Une erreur s'est produite",
        }
      }
    } else {
      etapeAvis.value = { status: 'LOADED', value: [] }
    }
    if (etapeAvis.value.status === 'LOADED') {
      props.onChange(etapeAvis.value.value)
    }
  })

  return () => <LoadingElement data={etapeAvis.value} renderItem={avis => <EtapeAvisLoaded avis={avis} {...props} />} />
})

type EtapeAvisLoadedProps = { avis: EtapeAvis[] } & Props
const EtapeAvisLoaded = defineComponent<EtapeAvisLoadedProps>(props => {
  const etapeAvis = ref<EtapeAvisModificationWithIndex[]>(props.avis.map((avis, index) => ({ ...avis, index })))

  watch(
    () => etapeAvis.value,
    () => {
      props.onChange(etapeAvis.value)
    },
    { deep: true }
  )

  const addOrEditPopupOpen = ref<{ open: true; required: boolean; avisTypeIds: NonEmptyArray<AvisTypeId>; etapeAvis?: (EtapeAvis | TempEtapeAvis) & WithIndex } | { open: false }>({ open: false })

  const avisTypes = computed(() => {
    return Object.values(
      getAvisTypes(props.tde.etapeTypeId, props.tde.titreTypeId, props.tde.demarcheTypeId, props.tde.demarcheId, props.tde.firstEtapeDate, props.communeIds, isArmMecanise(props.contenu))
    )
  })

  const completeRequiredAvis = computed<PropsTable['avis']>(() => {
    const avis: PropsTable['avis'] = etapeAvis.value.filter(({ avis_type_id }) => avisTypes.value.some(dt => dt.id === avis_type_id && !dt.optionnel))

    return avis
  })
  const emptyRequiredAvis = computed<AvisTypeId[]>(() => {
    const avis = avisTypes.value.filter(({ optionnel, id }) => !optionnel && !completeRequiredAvis.value.some(({ avis_type_id }) => avis_type_id === id)).map(({ id }) => id)

    return avis
  })
  const additionnalAvisTypeIds = computed<AvisTypeId[]>(() => {
    return avisTypes.value.filter(dt => dt.optionnel).map(({ id }) => id)
  })

  const additionnalAvis = computed<PropsTable['avis']>(() => {
    return etapeAvis.value.filter(({ avis_type_id }) => avisTypes.value.some(dt => dt.id === avis_type_id && dt.optionnel))
  })
  const openAddPopupAdditionnalAvis = () => {
    if (isNonEmptyArray(additionnalAvisTypeIds.value)) {
      addOrEditPopupOpen.value = { open: true, required: false, avisTypeIds: additionnalAvisTypeIds.value }
    }
  }
  const closeAddPopup = (newAvis: EtapeAvisModification | null) => {
    if (newAvis !== null && addOrEditPopupOpen.value.open) {
      const index = addOrEditPopupOpen.value.etapeAvis?.index
      if (isNullOrUndefined(index)) {
        etapeAvis.value.push({ ...newAvis, index: etapeAvis.value.length })
      } else {
        etapeAvis.value[index] = { ...newAvis, index }
      }
    }

    addOrEditPopupOpen.value = { open: false }
  }

  const addRequiredAvis = (avisTypeId: AvisTypeId) => {
    addOrEditPopupOpen.value = { open: true, required: true, avisTypeIds: [avisTypeId] }
  }
  const addOptionnalAvis = (avisTypeId: AvisTypeId) => {
    addOrEditPopupOpen.value = { open: true, required: false, avisTypeIds: [avisTypeId] }
  }
  const editRequiredAvis = (avisIndex: number) => {
    const avis = etapeAvis.value[avisIndex]
    addOrEditPopupOpen.value = { open: true, required: true, avisTypeIds: [avis.avis_type_id], etapeAvis: avis }
  }
  const editOptionnalAvis = (avisIndex: number) => {
    const avis = etapeAvis.value[avisIndex]
    addOrEditPopupOpen.value = { open: true, required: false, avisTypeIds: [avis.avis_type_id], etapeAvis: avis }
  }
  const removeAvis = (avisIndex: number) => {
    etapeAvis.value.splice(avisIndex, 1)
    etapeAvis.value.forEach((a, index) => {
      a.index = index
    })
  }

  const getNom = (avisTypeId: AvisTypeId) => {
    return capitalize(getAvisNom(avisTypeId))
  }

  return () => (
    <>
      {isNotNullNorUndefinedNorEmpty(emptyRequiredAvis.value) || isNotNullNorUndefinedNorEmpty(completeRequiredAvis.value) ? (
        <EtapeAvisTable
          getNom={getNom}
          add={addRequiredAvis}
          edit={editRequiredAvis}
          delete={removeAvis}
          caption="Avis obligatoires"
          emptyRequiredAvis={emptyRequiredAvis.value}
          avis={completeRequiredAvis.value}
        />
      ) : null}

      {isNonEmptyArray(additionnalAvisTypeIds.value) ? (
        <>
          <div style={{ display: 'flex', flexDirection: 'column' }} class="fr-mt-3w">
            <EtapeAvisTable getNom={getNom} add={addOptionnalAvis} edit={editOptionnalAvis} delete={removeAvis} caption="Avis complémentaires" emptyRequiredAvis={[]} avis={additionnalAvis.value} />
            <DsfrButtonIcon
              style={{ alignSelf: 'end' }}
              class="fr-mt-1w"
              icon="fr-icon-add-line"
              buttonType="secondary"
              title="Ajouter un avis complémentaire"
              label="Ajouter"
              onClick={openAddPopupAdditionnalAvis}
            />
          </div>
        </>
      ) : null}
      {addOrEditPopupOpen.value.open ? (
        <AddEtapeAvisPopup
          avisTypeIds={addOrEditPopupOpen.value.avisTypeIds}
          required={addOrEditPopupOpen.value.required}
          user={props.user}
          apiClient={props.apiClient}
          close={closeAddPopup}
          initialAvis={addOrEditPopupOpen.value.etapeAvis || null}
        />
      ) : null}
    </>
  )
})

type PropsTable = {
  caption: string
  avis: ((EtapeAvis | TempEtapeAvis) & { index: number })[]
  emptyRequiredAvis: AvisTypeId[]
  getNom: (avisTypeId: AvisTypeId) => string
  add: (avisTypeId: AvisTypeId) => void
  edit: (avisIndex: number) => void
  delete: (avisIndex: number) => void
}
const EtapeAvisTable: FunctionalComponent<PropsTable> = (props: PropsTable) => {
  const deleteAvis = (index: number) => () => {
    props.delete(index)
  }
  const editAvis = (index: number) => () => {
    props.edit(index)
  }

  const orderedAvis = [...props.avis].sort((a, b) => b.date.localeCompare(a.date))

  type ColumnId = 'nom' | 'date' | 'description' | 'statut' | 'visibilite' | 'actions'

  const columns: Column<ColumnId>[] = [
    { id: 'nom', contentTitle: 'Nom' },
    { id: 'date', contentTitle: 'Date' },
    { id: 'description', contentTitle: 'Description' },
    { id: 'statut', contentTitle: 'Statut' },
    { id: 'visibilite', contentTitle: 'Visibilité' },
    { id: 'actions', contentTitle: 'Action', class: [fr.cx('fr-cell--right')] },
  ]
  const rows: TableRow<ColumnId>[] = orderedAvis.map<TableRow<ColumnId>>(avis => {
    return {
      id: `${avis.index}`,
      link: null,
      columns: {
        nom: { type: 'text', value: props.getNom(avis.avis_type_id) },
        date: { type: 'text', value: dateFormat(avis.date) },
        description: { type: 'text', value: avis.description },
        statut: { type: 'jsx', value: avis.index, jsxElement: <AvisStatut avisStatutId={avis.avis_statut_id} /> },
        visibilite: { type: 'text', value: getAvisVisibilityLabel(avis.avis_visibility_id) },
        actions: {
          type: 'jsx',
          value: avis.index,
          jsxElement: (
            <div style={{ display: 'flex', justifyContent: 'end', alignItems: 'center' }}>
              <DsfrButtonIcon icon="fr-icon-edit-line" title={`Modifier l’avis de ${props.getNom(avis.avis_type_id)}`} onClick={editAvis(avis.index)} buttonType="secondary" buttonSize="sm" />
              <DsfrButtonIcon
                icon="fr-icon-delete-bin-line"
                class="fr-ml-1w"
                title={`Supprimer l’avis de ${props.getNom(avis.avis_type_id)}`}
                onClick={deleteAvis(avis.index)}
                buttonType="secondary"
                buttonSize="sm"
              />
            </div>
          ),
        },
      },
    }
  })

  rows.push(
    ...props.emptyRequiredAvis.map<TableRow<ColumnId>>((avisTypeId, index) => ({
      id: `${avisTypeId}-${index}`,
      link: null,
      columns: {
        nom: { type: 'text', value: props.getNom(avisTypeId), class: [fr.cx('fr-label--disabled')] },
        date: { type: 'text', value: '-' },
        description: { type: 'text', value: '-' },
        statut: { type: 'text', value: '-' },
        visibilite: { type: 'text', value: '-' },
        actions: {
          type: 'jsx',
          value: avisTypeId,
          jsxElement: (
            <DsfrButtonIcon icon="fr-icon-add-line" title={`Ajouter un document '${props.getNom(avisTypeId)}'`} onClick={() => props.add(avisTypeId)} buttonType="secondary" buttonSize="sm" />
          ),
        },
      },
    }))
  )

  return <TableSimple caption={{ value: props.caption, visible: true }} rows={rows} columns={columns} />
}

// @ts-ignore waiting for https://github.com/vuejs/core/issues/7833
EtapeAvisEdit.props = ['tde', 'onChange', 'etapeId', 'contenu', 'apiClient', 'communeIds', 'user']
// @ts-ignore waiting for https://github.com/vuejs/core/issues/7833
EtapeAvisLoaded.props = ['tde', 'onChange', 'etapeId', 'contenu', 'apiClient', 'avis', 'communeIds', 'user']
