import { sql } from '@pgtyped/runtime'
import { EffectDbQueryAndValidateErrors, Redefine, effectDbQueryAndValidate } from '../../pg-database.js'
import { z } from 'zod'
import { Pool } from 'pg'
import { User } from 'camino-common/src/roles.js'
import { canCreateTitre } from 'camino-common/src/permissions/titres.js'
import { TitreId, TitreSlug } from 'camino-common/src/validators/titres.js'
import { Effect } from 'effect'
import { newDemarcheId, newDemarcheSlug, newTitreId, newTitreSlug } from '../../database/models/_format/id-create.js'
import { CaminoError } from 'camino-common/src/zod-tools.js'
import { ICreateTitreDemarcheInternalQuery, ICreateTitreInternalQuery } from './titre-demande.queries.types.js'
import { TitreDemande } from 'camino-common/src/titres.js'
import { DemarcheId, DemarcheSlug } from 'camino-common/src/demarche.js'
import { DemarcheTypeId } from 'camino-common/src/static/demarchesTypes.js'
import { CaminoMachineId } from 'camino-common/src/validators/machine.js'
import { MachineInfo } from 'camino-common/src/machines.js'
import { TitreTypeId } from 'camino-common/src/static/titresTypes.js'
import { firstEtapeDateValidator, getCurrent } from 'camino-common/src/date.js'

type CreateTitreInput = Omit<TitreDemande, 'titreFromIds' | 'entrepriseId'>

const creationTitreImpossible = 'Création du titre impossible' as const
export type CreateTitreErrors = EffectDbQueryAndValidateErrors | 'Accès interdit' | typeof creationTitreImpossible
export const createTitre = (pool: Pool, user: User, titreInput: CreateTitreInput): Effect.Effect<TitreId, CaminoError<CreateTitreErrors>> =>
  Effect.Do.pipe(
    Effect.filterOrFail(
      () => canCreateTitre(user, titreInput.titreTypeId),
      () => ({ message: 'Accès interdit' as const })
    ),
    Effect.bind('id', () => Effect.succeed(newTitreId())),
    Effect.bind('slug', () =>
      Effect.try({
        try: () => newTitreSlug(titreInput.titreTypeId, titreInput.nom),
        catch: unknown => ({ message: creationTitreImpossible, extra: unknown }),
      })
    ),
    Effect.tap(({ id, slug }) => effectDbQueryAndValidate(createTitreInternal, { ...titreInput, id, slug, references: JSON.stringify(titreInput.references) }, pool, z.void())),
    Effect.map(({ id }) => id)
  )

const createTitreInternal = sql<Redefine<ICreateTitreInternalQuery, { id: TitreId; slug: TitreSlug; references: string } & Omit<CreateTitreInput, 'references'>, void>>`
INSERT INTO titres (id, slug, nom, type_id, "references")
    VALUES ($ id !, $slug!, $ nom !, $ titreTypeId!, $references!)
`

export type CreateDemarcheErrors = EffectDbQueryAndValidateErrors
export const createDemarche = (
  pool: Pool,
  titreId: TitreId,
  titreTypeId: TitreTypeId,
  demarcheTypeId: DemarcheTypeId,
  description: string = ''
): Effect.Effect<DemarcheId, CaminoError<CreateDemarcheErrors>> => {
  return Effect.Do.pipe(
    Effect.let('id', () => newDemarcheId()),
    Effect.let('slug', () => newDemarcheSlug(titreId, demarcheTypeId)),
    Effect.let('machineId', ({ id }) => MachineInfo.withDate(titreTypeId, demarcheTypeId, id, firstEtapeDateValidator.parse(getCurrent())).machineId ?? null),
    Effect.tap(({ id, slug, machineId }) => effectDbQueryAndValidate(createTitreDemarcheInternal, { id, slug, titre_id: titreId, demarcheTypeId, machineId, description }, pool, z.void())),
    Effect.map(({ id }) => id)
  )
}
const createTitreDemarcheInternal = sql<
  Redefine<ICreateTitreDemarcheInternalQuery, { id: DemarcheId; slug: DemarcheSlug; titre_id: TitreId; demarcheTypeId: DemarcheTypeId; machineId: CaminoMachineId | null; description: string }, void>
>`
INSERT INTO titres_demarches (id, slug, titre_id, type_id, machine_id, description)
    VALUES ($ id !, $slug!, $ titre_id !, $demarcheTypeId!, $machineId!, $description)
`
