import { EtapeBrouillon, EtapeDocument, EtapeDocumentModification, EtapeId, GetEtapeDocumentsByEtapeId, TempEtapeDocument } from 'camino-common/src/etape'
import { EtapeTypeId } from 'camino-common/src/static/etapesTypes'
import { ApiClient } from '../../api/api-client'
import { FunctionalComponent, computed, defineComponent, onMounted, ref, watch } from 'vue'
import { SDOMZoneId } from 'camino-common/src/static/sdom'
import { isNonEmptyArray, isNotNullNorUndefined, isNotNullNorUndefinedNorEmpty, isNullOrUndefined, NonEmptyArray } from 'camino-common/src/typescript-tools'
import { AutreDocumentType, AutreDocumentTypeId, DocumentType, DocumentTypeId, DocumentsTypes } from 'camino-common/src/static/documentsTypes'
import { LoadingElement } from '../_ui/functional-loader'
import { AsyncData, asyncDataAutomaticLoad } from '../../api/client-rest'
import { DsfrButtonIcon } from '../_ui/dsfr-button'
import { getVisibilityLabel, sortDocumentsColumn } from './etape-documents'
import { AddEtapeDocumentPopup } from './add-etape-document-popup'
import { User } from 'camino-common/src/roles'
import { FlattenedContenu } from 'camino-common/src/etape-form'
import { fr } from '@codegouvfr/react-dsfr'
import { Column, TableSimple } from '../_ui/table-simple'
import { TableRow } from '../_ui/table'
import { getDocuments } from 'camino-common/src/static/titresTypes_demarchesTypes_etapesTypes/documents'
import { isArmMecanise } from 'camino-common/src/static/mecanise'
import { useState } from '@/utils/vue-tsx-utils'
import { MachineInfo } from 'camino-common/src/machines'

interface Props {
  tde: {
    etapeTypeId: EtapeTypeId
    machineInfo: MachineInfo
  }
  isBrouillon: EtapeBrouillon
  sdomZoneIds: SDOMZoneId[]
  completeUpdate: (etapeDocuments: (EtapeDocument | TempEtapeDocument)[]) => void
  etapeId: EtapeId | null
  apiClient: Pick<ApiClient, 'uploadTempDocument' | 'getEtapeDocumentsByEtapeId'>
  contenu: FlattenedContenu
  user: User
}

type WithIndex = { index: number }

type EtapeDocumentModificationWithIndex = EtapeDocumentModification & WithIndex
export const EtapeDocumentsEdit = defineComponent<Props>(props => {
  const [etapeDocuments, setEtapeDocuments] = useState<AsyncData<GetEtapeDocumentsByEtapeId>>({ status: 'LOADING' })

  onMounted(async () => {
    await asyncDataAutomaticLoad(() => {
      if (isNotNullNorUndefined(props.etapeId)) {
        return props.apiClient.getEtapeDocumentsByEtapeId(props.etapeId)
      }
      return Promise.resolve({ etapeDocuments: [] })
    }, setEtapeDocuments)

    if (etapeDocuments.value.status === 'LOADED') {
      props.completeUpdate(etapeDocuments.value.value.etapeDocuments)
    }
  })

  return () => <LoadingElement data={etapeDocuments.value} renderItem={items => <EtapeDocumentsLoaded {...items} {...props} />} />
})

type EtapeDocumentsLoadedProps = GetEtapeDocumentsByEtapeId & Props
const EtapeDocumentsLoaded = defineComponent<EtapeDocumentsLoadedProps>(props => {
  const etapeDocuments = ref<EtapeDocumentModificationWithIndex[]>(props.etapeDocuments.map((document, index) => ({ ...document, index })))

  watch(
    () => etapeDocuments.value,
    () => {
      props.completeUpdate(etapeDocuments.value)
    },
    { deep: true }
  )

  const addOrEditPopupOpen = ref<{ open: true; documentTypeIds: NonEmptyArray<DocumentTypeId | AutreDocumentTypeId>; document?: (EtapeDocument | TempEtapeDocument) & WithIndex } | { open: false }>({
    open: false,
  })

  const documentTypes = computed<(DocumentType | AutreDocumentType)[]>(() => {
    return getDocuments(props.tde.machineInfo, props.tde.etapeTypeId, props.sdomZoneIds, isArmMecanise(props.contenu))
  })

  const completeRequiredDocuments = computed<PropsTable['documents']>(() => {
    const documents: PropsTable['documents'] = etapeDocuments.value.filter(
      ({ etape_document_type_id, type }) => type !== 'NON_RENSEIGNE' && documentTypes.value.some(dt => dt.id === etape_document_type_id && !dt.optionnel)
    )

    return documents
  })
  const emptyRequiredDocuments = computed<({ etape_document_type_id: DocumentTypeId | AutreDocumentTypeId } | NonRenseigneEmptyRequiredDocument)[]>(() => {
    const documents: { etape_document_type_id: DocumentTypeId | AutreDocumentTypeId }[] = documentTypes.value
      .filter(({ optionnel, id }) => !optionnel && !etapeDocuments.value.some(({ etape_document_type_id }) => etape_document_type_id === id))
      .map(({ id }) => ({ etape_document_type_id: id }))

    const nonRenseigneDocs: NonRenseigneEmptyRequiredDocument[] = etapeDocuments.value
      .map(({ etape_document_type_id, type, index }) => {
        if (type === 'NON_RENSEIGNE') {
          return { type, etape_document_type_id, index }
        } else {
          return null
        }
      })
      .filter(isNotNullNorUndefined)
    return [...documents, ...nonRenseigneDocs]
  })
  const additionnalDocumentTypeIds = computed<(DocumentTypeId | AutreDocumentTypeId)[]>(() => {
    return documentTypes.value.filter(dt => dt.optionnel).map(({ id }) => id)
  })

  const additionnalDocuments = computed<PropsTable['documents']>(() => {
    return etapeDocuments.value.filter(({ etape_document_type_id }) => documentTypes.value.some(dt => dt.id === etape_document_type_id && dt.optionnel))
  })
  const openAddPopupAdditionnalDocument = () => {
    if (isNonEmptyArray(additionnalDocumentTypeIds.value)) {
      addOrEditPopupOpen.value = { open: true, documentTypeIds: additionnalDocumentTypeIds.value }
    }
  }
  const closeAddPopup = (newDocument: EtapeDocumentModification | null) => {
    if (newDocument !== null && addOrEditPopupOpen.value.open) {
      const index = addOrEditPopupOpen.value.document?.index
      if (isNullOrUndefined(index)) {
        etapeDocuments.value.push({ ...newDocument, index: etapeDocuments.value.length })
      } else {
        etapeDocuments.value[index] = { ...newDocument, index }
      }
    }

    addOrEditPopupOpen.value = { open: false }
  }

  const addDocument = (documentTypeId: DocumentTypeId | AutreDocumentTypeId) => {
    addOrEditPopupOpen.value = { open: true, documentTypeIds: [documentTypeId] }
  }
  const editDocument = (documentIndex: number) => {
    const document = etapeDocuments.value[documentIndex]
    addOrEditPopupOpen.value = { open: true, documentTypeIds: [document.etape_document_type_id], document }
  }
  const removeDocument = (documentIndex: number) => {
    etapeDocuments.value.splice(documentIndex, 1)
    // On recalcule les index
    etapeDocuments.value.forEach((a, index) => {
      a.index = index
    })
  }

  const getNom = (documentTypeId: DocumentTypeId | AutreDocumentTypeId) => {
    return DocumentsTypes[documentTypeId].nom
  }

  return () => (
    <>
      {isNotNullNorUndefinedNorEmpty(emptyRequiredDocuments.value) || isNotNullNorUndefinedNorEmpty(completeRequiredDocuments.value) ? (
        <EtapeDocumentsTable
          getNom={getNom}
          add={addDocument}
          edit={editDocument}
          delete={removeDocument}
          caption="Documents obligatoires"
          emptyRequiredDocuments={emptyRequiredDocuments.value}
          documents={completeRequiredDocuments.value}
          isBrouillon={props.isBrouillon}
          user={props.user}
        />
      ) : null}

      {isNonEmptyArray(additionnalDocumentTypeIds.value) ? (
        <>
          <div style={{ display: 'flex', flexDirection: 'column' }} class="fr-mt-3w">
            <EtapeDocumentsTable
              getNom={getNom}
              add={addDocument}
              edit={editDocument}
              delete={removeDocument}
              caption="Documents complémentaires"
              emptyRequiredDocuments={[]}
              documents={additionnalDocuments.value}
              isBrouillon={props.isBrouillon}
              user={props.user}
            />
            <DsfrButtonIcon
              style={{ alignSelf: 'end' }}
              class="fr-mt-1w"
              icon="fr-icon-add-line"
              buttonType="secondary"
              title="Ajouter un document complémentaire"
              label="Ajouter"
              onClick={openAddPopupAdditionnalDocument}
            />
          </div>
        </>
      ) : null}

      {addOrEditPopupOpen.value.open ? (
        <AddEtapeDocumentPopup
          documentTypeIds={addOrEditPopupOpen.value.documentTypeIds}
          apiClient={props.apiClient}
          close={closeAddPopup}
          user={props.user}
          initialDocument={addOrEditPopupOpen.value.document}
        />
      ) : null}
    </>
  )
})

interface NonRenseigneEmptyRequiredDocument {
  type: 'NON_RENSEIGNE'
  etape_document_type_id: DocumentTypeId
  index: number
}
type PropsTable = {
  caption: string
  documents: ((EtapeDocument | TempEtapeDocument) & { index: number })[]
  isBrouillon: EtapeBrouillon
  emptyRequiredDocuments: ({ etape_document_type_id: DocumentTypeId | AutreDocumentTypeId } | NonRenseigneEmptyRequiredDocument)[]
  getNom: (documentTypeId: DocumentTypeId | AutreDocumentTypeId) => string
  add: (documentTypeId: DocumentTypeId | AutreDocumentTypeId) => void
  edit: (documentIndex: number) => void
  delete: (documentIndex: number) => void
  user: User
}
const EtapeDocumentsTable: FunctionalComponent<PropsTable> = (props: PropsTable) => {
  const deleteDocument = (index: number) => () => {
    props.delete(index)
  }
  const editDocument = (index: number) => () => {
    props.edit(index)
  }
  const sortedDocuments = computed(() => sortDocumentsColumn(props.documents.map(d => ({ ...d, document_type_id: d.etape_document_type_id }))))
  const sortedEmptyRequiredDocuments = computed(() =>
    [...props.emptyRequiredDocuments].sort((a, b) => DocumentsTypes[a.etape_document_type_id].nom.localeCompare(DocumentsTypes[b.etape_document_type_id].nom))
  )

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

  const columns: Column<ColumnId>[] = [
    { id: 'nom', contentTitle: 'Nom' },
    { id: 'description', contentTitle: 'Description' },
    { id: 'visibilite', contentTitle: 'Visibilité' },
    { id: 'actions', contentTitle: 'Action', class: [fr.cx('fr-cell--right')] },
  ]

  const rows: TableRow<ColumnId>[] = sortedDocuments.value.map<TableRow<ColumnId>>(document => ({
    id: `${document.index}`,
    link: null,
    columns: {
      nom: { type: 'text', value: props.getNom(document.etape_document_type_id) },
      description: { type: 'text', value: document.description ?? '' },
      visibilite: { type: 'text', value: getVisibilityLabel(document) },
      actions: {
        type: 'jsx',
        value: document.index,
        jsxElement: (
          <div style={{ display: 'flex', justifyContent: 'end', alignItems: 'center' }}>
            <DsfrButtonIcon
              icon="fr-icon-edit-line"
              title={`Modifier le document de ${props.getNom(document.etape_document_type_id)}`}
              onClick={editDocument(document.index)}
              buttonType="secondary"
              buttonSize="sm"
            />
            <DsfrButtonIcon
              icon="fr-icon-delete-bin-line"
              class="fr-ml-1w"
              title={`Supprimer le document de ${props.getNom(document.etape_document_type_id)}`}
              onClick={deleteDocument(document.index)}
              buttonType="secondary"
              buttonSize="sm"
            />
          </div>
        ),
      },
    },
  }))

  rows.push(
    ...sortedEmptyRequiredDocuments.value
      .filter(document => !('type' in document))
      .map<TableRow<ColumnId>>(documentTypeId => ({
        id: documentTypeId.etape_document_type_id,
        link: null,
        columns: {
          nom: { type: 'text', value: props.getNom(documentTypeId.etape_document_type_id), class: [fr.cx('fr-label--disabled')] },
          description: { type: 'text', value: '-' },
          visibilite: { type: 'text', value: '-' },
          actions: {
            type: 'jsx',
            value: documentTypeId.etape_document_type_id,
            jsxElement: (
              <DsfrButtonIcon
                icon="fr-icon-add-line"
                title={`Ajouter un document ${props.getNom(documentTypeId.etape_document_type_id)}`}
                onClick={() => props.add(documentTypeId.etape_document_type_id)}
                buttonType="secondary"
                buttonSize="sm"
              />
            ),
          },
        },
      })),
    ...sortedEmptyRequiredDocuments.value
      .filter((document): document is NonRenseigneEmptyRequiredDocument => 'type' in document && document.type === 'NON_RENSEIGNE')
      .map<TableRow<ColumnId>>(document => ({
        id: document.etape_document_type_id,
        link: null,
        columns: {
          nom: { type: 'jsx', value: props.getNom(document.etape_document_type_id), jsxElement: <span class={fr.cx('fr-label--error')}>{props.getNom(document.etape_document_type_id)}</span> },
          description: { type: 'jsx', value: 'Fichier manquant', jsxElement: <span class={fr.cx('fr-label--error')}>Fichier manquant</span> },
          visibilite: { type: 'text', value: '-' },
          actions: {
            type: 'jsx',
            value: document.etape_document_type_id,
            jsxElement: (
              <DsfrButtonIcon
                icon="fr-icon-add-line"
                title={`Ajouter un document ${props.getNom(document.etape_document_type_id)}`}
                onClick={editDocument(document.index)}
                buttonType="secondary"
                buttonSize="sm"
              />
            ),
          },
        },
      }))
  )

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

// @ts-ignore waiting for https://github.com/vuejs/core/issues/7833
EtapeDocumentsEdit.props = ['tde', 'completeUpdate', 'etapeId', 'apiClient', 'sdomZoneIds', 'contenu', 'isBrouillon', 'user']
// @ts-ignore waiting for https://github.com/vuejs/core/issues/7833
EtapeDocumentsLoaded.props = ['tde', 'completeUpdate', 'etapeId', 'apiClient', 'sdomZoneIds', 'contenu', 'isBrouillon', 'user', 'etapeDocuments']
