package com.prodhack.moscow2025.data.repImplementations

import androidx.paging.map
import com.prodhack.moscow2025.data.base.BaseRepository
import com.prodhack.moscow2025.data.data_providers.api.ApiKtorClient
import com.prodhack.moscow2025.data.data_providers.local_db.AppDatabase
import com.prodhack.moscow2025.data.data_providers.local_db.entities.ResumeHistoryEntity
import com.prodhack.moscow2025.data.dto.ResumeCreateDTO
import com.prodhack.moscow2025.data.dto.ResumeDTO
import com.prodhack.moscow2025.data.dto.ResumeIdDTO
import com.prodhack.moscow2025.data.dto.ResumeListDTO
import com.prodhack.moscow2025.data.dto.ResumeSkillDTO
import com.prodhack.moscow2025.data.dto.mapToData
import com.prodhack.moscow2025.domain.interfaces.resumes.ResumeRepository
import com.prodhack.moscow2025.domain.models.ResumeCreationModel
import com.prodhack.moscow2025.domain.models.ResumeModel
import com.prodhack.moscow2025.domain.utils.NetworkError
import com.prodhack.moscow2025.domain.utils.RemotePagingWrapper
import io.ktor.client.request.setBody
import io.ktor.client.request.url
import io.ktor.http.ContentType
import io.ktor.http.HttpMethod
import io.ktor.http.contentType
import io.ktor.http.path
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import org.koin.core.annotation.Single
import kotlin.collections.map

@Single
class ResumeRepositoryImpl(
	ktorClient: ApiKtorClient,
	override val db: AppDatabase
) : ResumeRepository, BaseRepository() {

	override val defaultKtorClient = ktorClient.client

	private val resumeDao = db.resumeDao()
	private val resumeHistoryDao = db.resumeHistoryDao()

	override fun loadResumeList(): RemotePagingWrapper<ResumeModel> = paginatedRequest(
		pageSize = 20,
		dbDao = resumeDao,
		makeRequest = { offset, pageSize ->
			networkRequest<ResumeListDTO> {
				method = HttpMethod.Get
				url {
					url("/resume/list")
					parameters.append("limit", pageSize.toString())
					parameters.append("offset", offset.toString())
				}
			}.map { it -> it.resumes.map { it.mapToDB() } }
		}
	).map { it -> it.map { it.mapToDomain() } }

	override suspend fun suggestSkills(query: String): Result<List<String>> =
		networkRequest<List<ResumeSkillDTO>> {
			method = HttpMethod.Get
			url {
				path("key_skills")
				parameters.append("query", query)
			}
		}.map { it.map { it.name } }

	override suspend fun createResume(resumeForm: ResumeCreationModel): Result<String> =
		networkRequest<ResumeIdDTO> {
			method = HttpMethod.Post

			url {
				url("/resume")
			}

			setBody(resumeForm.mapToData())
			contentType(ContentType.Application.Json)
		}.map { it.resumeId }

	override suspend fun updateResume(
		resumeId: String,
		resumeForm: ResumeCreationModel
	): Result<String> = networkRequest<ResumeDTO> {
		method = HttpMethod.Patch

		url {
			path("resume", resumeId)
		}

		setBody(resumeForm.mapToData())
		contentType(ContentType.Application.Json)
	}.map {
		resumeDao.upsertAll(
			listOf(
				it.mapToDB()
			)
		)
		resumeDao.deleteWithId(resumeId)
		it.id
	}

	override suspend fun refreshResume(resumeId: String): Result<ResumeModel> =
		networkRequest<ResumeDTO> {
			method = HttpMethod.Get
			url {
				path("resume", resumeId)
			}
		}.map {
			it.mapToDomain().also { model ->
				resumeDao.upsertAll(listOf(it.mapToDB()))
				resumeHistoryDao.upsertAll(listOf(ResumeHistoryEntity.fromDomain(model)))
			}
		}

	override fun getResume(resumeId: String): Flow<Result<ResumeModel>> =
		merge(
			resumeDao.getById(resumeId = resumeId).map { entity ->
				entity?.let { Result.success(it.mapToDomain()) }
					?: Result.failure(NetworkError.InputError("Резюме не найдено"))
			},
			flow {
				emit(networkRequest<ResumeDTO> {
					method = HttpMethod.Get
					url {
						path("resume", resumeId)
					}
				}.map {
					it.also {
						resumeDao.upsertAll(listOf(it.mapToDB()))
					}.mapToDomain()
				}
				)
			}
		)

	override fun loadResumeHistory(resumeId: String): RemotePagingWrapper<ResumeModel> =
		paginatedRequest(
			pageSize = 10,
			dbDao = resumeHistoryDao,
			makeRequest = { offset, pageSize ->
				fetchResumeHistoryPage(resumeId, offset, pageSize).map { list ->
					list.map { ResumeHistoryEntity.fromDomain(it) }
				}
			}
		).map { pagingData -> pagingData.map { it.mapToDomain() } }

	private suspend fun fetchResumeHistoryPage(
		resumeId: String,
		offset: Long,
		pageSize: Int
	): Result<List<ResumeModel>> = networkRequest<ResumeListDTO> {
		method = HttpMethod.Get
		url {
			path("resume", resumeId, "history")
			parameters.append("limit", pageSize.toString())
			parameters.append("offset", offset.toString())
		}
	}.map { it -> it.resumes.map { it.mapToDomain() } }
}
