package de.kif.backend.util

import de.kif.backend.database.Connection
import de.kif.backend.repository.*
import de.kif.common.Message
import de.kif.common.RepositoryType
import de.kif.common.model.*
import kotlinx.serialization.Serializable

@Serializable
data class Backup(
    val posts: List<Post> = emptyList(),
    val rooms: List<Room> = emptyList(),
    val schedules: List<Schedule> = emptyList(),
    val tracks: List<Track> = emptyList(),
    val users: List<User> = emptyList(),
    val workGroups: List<WorkGroup> = emptyList()
) {
    companion object {
        suspend fun backup(vararg repositories: RepositoryType): String {
            var backup = Backup()

            val repositorySet = repositories.toMutableSet()

            if (RepositoryType.SCHEDULE in repositorySet) {
                repositorySet += RepositoryType.ROOM
                repositorySet += RepositoryType.WORK_GROUP
            }

            if (RepositoryType.WORK_GROUP in repositorySet) {
                repositorySet += RepositoryType.TRACK
            }

            for (repository in repositorySet) {
                backup = when (repository) {
                    RepositoryType.ROOM -> backup.copy(rooms = RoomRepository.all())
                    RepositoryType.SCHEDULE -> backup.copy(schedules = ScheduleRepository.all())
                    RepositoryType.TRACK -> backup.copy(tracks = TrackRepository.all())
                    RepositoryType.USER -> backup.copy(users = UserRepository.all())
                    RepositoryType.WORK_GROUP -> backup.copy(workGroups = WorkGroupRepository.all())
                    RepositoryType.POST -> backup.copy(posts = PostRepository.all())
                }
            }

            return Message.json.stringify(serializer(), backup)
        }

        @Suppress("UNUSED_VARIABLE")
        suspend fun import(data: String) {
            val backup = Message.json.parse(serializer(), data)

            val userMap = backup.users.associateWith { UserRepository.create(it) }
            val postMap = backup.posts.associateWith { PostRepository.create(it) }

            val roomMap = backup.rooms.associateWith { RoomRepository.create(it) }
            val trackMap = backup.tracks.associateWith { TrackRepository.create(it) }
            val workGroupMap = backup.workGroups.associateWith {
                var workGroup = it
                val track = workGroup.track
                if (track != null) {
                    workGroup = workGroup.copy(track = track.copy(id = trackMap[track] ?: return@associateWith -1L))
                }

                WorkGroupRepository.create(workGroup)
            }
            val scheduleMap = backup.schedules.associateWith {
                ScheduleRepository.create(
                    it.copy(
                        room = it.room.copy(id = roomMap[it.room] ?: return@associateWith -1L),
                        workGroup = it.workGroup.copy(id = workGroupMap[it.workGroup] ?: return@associateWith -1L)
                    )
                )
            }
        }

        suspend fun restore(data: String) {
            Connection.reset()

            import(data)
        }
    }
}
