package com.betriebsratkanzlei.legalconsenthub.application_form_version

import com.betriebsratkanzlei.legalconsenthub.application_form.ApplicationForm
import com.betriebsratkanzlei.legalconsenthub.application_form.ApplicationFormRepository
import com.betriebsratkanzlei.legalconsenthub.error.ApplicationFormNotFoundException
import com.betriebsratkanzlei.legalconsenthub.error.ApplicationFormVersionNotFoundException
import com.betriebsratkanzlei.legalconsenthub.form_element.FormElement
import com.betriebsratkanzlei.legalconsenthub.form_element.FormElementSection
import com.betriebsratkanzlei.legalconsenthub.form_element.FormOption
import com.betriebsratkanzlei.legalconsenthub.user.User
import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormSnapshotDto
import com.betriebsratkanzlei.legalconsenthub_api.model.FormElementSnapshotDto
import com.betriebsratkanzlei.legalconsenthub_api.model.FormElementSectionSnapshotDto
import com.betriebsratkanzlei.legalconsenthub_api.model.FormOptionDto
import com.fasterxml.jackson.databind.ObjectMapper
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.util.UUID

@Service
class ApplicationFormVersionService(
    private val versionRepository: ApplicationFormVersionRepository,
    private val applicationFormRepository: ApplicationFormRepository,
    private val objectMapper: ObjectMapper,
) {
    @Transactional
    fun createVersion(
        applicationForm: ApplicationForm,
        user: User,
    ): ApplicationFormVersion {
        val nextVersionNumber = versionRepository.countByApplicationFormId(applicationForm.id!!) + 1

        val snapshot = createSnapshot(applicationForm)
        val snapshotJson = objectMapper.writeValueAsString(snapshot)

        val version =
            ApplicationFormVersion(
                applicationForm = applicationForm,
                versionNumber = nextVersionNumber,
                name = applicationForm.name,
                status = applicationForm.status,
                organizationId = applicationForm.organizationId,
                snapshotData = snapshotJson,
                createdBy = user,
            )

        return versionRepository.save(version)
    }

    fun getVersionsByApplicationFormId(applicationFormId: UUID): List<ApplicationFormVersion> =
        versionRepository.findByApplicationFormIdOrderByVersionNumberDesc(applicationFormId)

    fun getVersion(
        applicationFormId: UUID,
        versionNumber: Int,
    ): ApplicationFormVersion =
        versionRepository
            .findByApplicationFormIdAndVersionNumber(applicationFormId, versionNumber)
            .orElseThrow { ApplicationFormVersionNotFoundException(applicationFormId, versionNumber) }

    @Transactional
    fun restoreVersion(
        applicationFormId: UUID,
        versionNumber: Int,
        user: User,
    ): ApplicationForm {
        val version = getVersion(applicationFormId, versionNumber)
        val applicationForm =
            applicationFormRepository
                .findById(applicationFormId)
                .orElseThrow { ApplicationFormNotFoundException(applicationFormId) }

        val snapshot = objectMapper.readValue(version.snapshotData, ApplicationFormSnapshotDto::class.java)

        applicationForm.name = snapshot.name
        applicationForm.status = snapshot.status
        applicationForm.organizationId = snapshot.organizationId
        applicationForm.lastModifiedBy = user

        applicationForm.formElementSections.clear()
        snapshot.sections.forEach { sectionSnapshot ->
            val section = createSectionFromSnapshot(sectionSnapshot, applicationForm)
            applicationForm.formElementSections.add(section)
        }

        val restoredForm = applicationFormRepository.save(applicationForm)

        createVersion(restoredForm, user)

        return restoredForm
    }

    private fun createSnapshot(applicationForm: ApplicationForm): ApplicationFormSnapshotDto =
        ApplicationFormSnapshotDto(
            name = applicationForm.name,
            status = applicationForm.status,
            organizationId = applicationForm.organizationId,
            sections =
                applicationForm.formElementSections.map { section ->
                    FormElementSectionSnapshotDto(
                        title = section.title,
                        shortTitle = section.shortTitle,
                        description = section.description,
                        elements =
                            section.formElements.map { element ->
                                FormElementSnapshotDto(
                                    title = element.title,
                                    description = element.description,
                                    type = element.type,
                                    options =
                                        element.options.map { option ->
                                            FormOptionDto(
                                                value = option.value,
                                                label = option.label,
                                                processingPurpose = option.processingPurpose,
                                                employeeDataCategory = option.employeeDataCategory,
                                            )
                                        },
                                )
                            },
                    )
                },
        )

    private fun createSectionFromSnapshot(
        sectionSnapshot: FormElementSectionSnapshotDto,
        applicationForm: ApplicationForm,
    ): FormElementSection {
        val section =
            FormElementSection(
                title = sectionSnapshot.title,
                shortTitle = sectionSnapshot.shortTitle,
                description = sectionSnapshot.description,
                applicationForm = applicationForm,
            )

        sectionSnapshot.elements.forEach { elementSnapshot ->
            val element =
                FormElement(
                    title = elementSnapshot.title,
                    description = elementSnapshot.description,
                    type = elementSnapshot.type,
                    formElementSection = section,
                    options =
                        elementSnapshot.options.map { optionDto ->
                            FormOption(
                                value = optionDto.value,
                                label = optionDto.label,
                                processingPurpose = optionDto.processingPurpose,
                                employeeDataCategory = optionDto.employeeDataCategory,
                            )
                        }.toMutableList(),
                )
            section.formElements.add(element)
        }

        return section
    }
}

