package id.iiyh.inatrading.feature.auth.data.repository

import android.content.Context
import android.net.Uri
import android.util.Base64
import android.webkit.MimeTypeMap
import androidx.core.net.toFile
import dagger.hilt.android.qualifiers.ApplicationContext
import id.iiyh.inatrading.core.data.local.SessionManager
import id.iiyh.inatrading.core.data.remote.ApiService
import id.iiyh.inatrading.feature.auth.data.model.RegisterRequest
import id.iiyh.inatrading.feature.auth.domain.repository.AuthRepository
import id.iiyh.inatrading.feature.profile.data.model.BuyerProfile
import id.iiyh.inatrading.feature.profile.data.model.CityItem
import id.iiyh.inatrading.feature.profile.data.model.ChangePasswordRequest
import id.iiyh.inatrading.feature.profile.data.model.CreateShippingAddressRequest
import id.iiyh.inatrading.feature.profile.data.model.ProvinceItem
import id.iiyh.inatrading.feature.profile.data.model.ShippingAddress
import id.iiyh.inatrading.feature.profile.data.model.UpdateBuyerProfileRequest
import java.io.File
import java.util.UUID
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody
import retrofit2.HttpException
import javax.inject.Inject

class AuthRepositoryImpl @Inject constructor(
    @ApplicationContext private val context: Context,
    private val apiService: ApiService,
    private val sessionManager: SessionManager,
) : AuthRepository {

    private companion object {
        const val REGION_PAGE_LIMIT = 100
    }

    override suspend fun login(email: String, password: String): Result<Unit> {
        return try {
            val credentials = Base64.encodeToString("$email:$password".toByteArray(), Base64.NO_WRAP)
            val basicAuth = "Basic $credentials"
            val response = apiService.loginBuyer(basicAuth)
            if (!response.isSuccess) {
                return Result.failure(Exception(response.responseDesc ?: "Login gagal"))
            }
            val data = response.data
                ?: return Result.failure(Exception("Session tidak ditemukan"))
            val session = data.session
                ?: return Result.failure(Exception("Session tidak ditemukan"))
            sessionManager.saveSession(
                token    = session,
                email    = email,
                name     = data.name.orEmpty(),
                userType = data.userType.orEmpty(),
            )
            Result.success(Unit)
        } catch (e: HttpException) {
            Result.failure(Exception(e.parseMessage()))
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    override suspend fun register(
        name: String,
        email: String,
        mobile: String,
        password: String,
    ): Result<Unit> {
        return try {
            val response = apiService.registerBuyer(
                RegisterRequest(name = name, email = email, mobile = mobile, password = password)
            )
            if (!response.isSuccess) {
                return Result.failure(Exception(response.responseDesc ?: "Registrasi gagal"))
            }
            Result.success(Unit)
        } catch (e: HttpException) {
            Result.failure(Exception(e.parseMessage()))
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    override suspend fun getBuyerProfile(): Result<BuyerProfile> {
        return try {
            val response = apiService.getBuyerProfile()
            if (!response.isSuccess) {
                return Result.failure(Exception(response.responseDesc ?: "Gagal memuat profil"))
            }
            val data = response.data
                ?: return Result.failure(Exception("Data profil tidak ditemukan"))
            sessionManager.updateProfile(
                name = data.name.orEmpty(),
                email = data.email.orEmpty(),
                avatarUrl = data.imageId.orEmpty(),
            )
            Result.success(data)
        } catch (e: HttpException) {
            Result.failure(Exception(e.parseMessage()))
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    override suspend fun getAddresses(): Result<List<ShippingAddress>> {
        return try {
            val response = apiService.getAddresses()
            if (!response.isSuccess) {
                return Result.failure(Exception(response.responseDesc ?: "Gagal memuat alamat pengiriman"))
            }
            Result.success(response.rows)
        } catch (e: HttpException) {
            Result.failure(Exception(e.parseMessage()))
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    override suspend fun getProvinces(): Result<List<ProvinceItem>> {
        return try {
            val provinces = mutableListOf<ProvinceItem>()
            var page = 1
            var totalPage = 1

            while (page <= totalPage) {
                val response = apiService.getProvinces(
                    page = page,
                    limit = REGION_PAGE_LIMIT,
                )
                if (!response.isSuccess) {
                    return Result.failure(Exception(response.responseDesc ?: "Gagal memuat provinsi"))
                }
                provinces += response.rows
                totalPage = response.totalPage.coerceAtLeast(1)
                page += 1
            }

            Result.success(
                provinces.distinctBy { it.id ?: it.code ?: it.name.orEmpty() }
            )
        } catch (e: HttpException) {
            Result.failure(Exception(e.parseMessage()))
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    override suspend fun getCities(provinceId: String): Result<List<CityItem>> {
        return try {
            val cities = mutableListOf<CityItem>()
            var page = 1
            var totalPage = 1

            while (page <= totalPage) {
                val response = apiService.getCities(
                    provinceId = provinceId,
                    page = page,
                    limit = REGION_PAGE_LIMIT,
                )
                if (!response.isSuccess) {
                    return Result.failure(Exception(response.responseDesc ?: "Gagal memuat kota"))
                }
                cities += response.rows
                totalPage = response.totalPage.coerceAtLeast(1)
                page += 1
            }

            Result.success(
                cities.distinctBy { it.id ?: it.code ?: it.name.orEmpty() }
            )
        } catch (e: HttpException) {
            Result.failure(Exception(e.parseMessage()))
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    override suspend fun createAddress(request: CreateShippingAddressRequest): Result<Unit> {
        return try {
            val response = apiService.createAddress(request)
            if (!response.isSuccess) {
                return Result.failure(Exception(response.responseDesc ?: "Gagal menyimpan alamat"))
            }
            Result.success(Unit)
        } catch (e: HttpException) {
            Result.failure(Exception(e.parseMessage()))
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    override suspend fun updateAddress(
        addressId: String,
        request: CreateShippingAddressRequest,
    ): Result<Unit> {
        return try {
            val response = apiService.updateAddress(addressId, request)
            if (!response.isSuccess) {
                return Result.failure(Exception(response.responseDesc ?: "Gagal memperbarui alamat"))
            }
            Result.success(Unit)
        } catch (e: HttpException) {
            Result.failure(Exception(e.parseMessage()))
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    override suspend fun deleteAddress(addressId: String): Result<Unit> {
        return try {
            val response = apiService.deleteAddress(addressId)
            if (!response.isSuccess) {
                return Result.failure(Exception(response.responseDesc ?: "Gagal menghapus alamat"))
            }
            Result.success(Unit)
        } catch (e: HttpException) {
            Result.failure(Exception(e.parseMessage()))
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    override suspend fun uploadFile(uri: Uri): Result<String> {
        return try {
            val file = copyUriToTempFile(uri)
            val mimeType = context.contentResolver.getType(uri).orEmpty().ifBlank { "image/*" }
            val requestBody = file.asRequestBody(mimeType.toMediaTypeOrNull())
            val part = MultipartBody.Part.createFormData("file", file.name, requestBody)
            val response = apiService.uploadFile(part)
            file.delete()
            if (!response.isSuccess) {
                return Result.failure(Exception(response.responseDesc ?: "Gagal mengunggah gambar"))
            }
            val fileId = response.data?.fileId
                ?: return Result.failure(Exception("File ID tidak ditemukan"))
            Result.success(fileId)
        } catch (e: HttpException) {
            Result.failure(Exception(e.parseMessage()))
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    override suspend fun updateBuyerProfile(
        name: String,
        mobile: String,
        imageId: String?,
        email: String,
        profileDescription: String?,
    ): Result<Unit> {
        return try {
            val response = apiService.updateBuyerProfile(
                UpdateBuyerProfileRequest(
                    email = email,
                    imageId = imageId,
                    mobile = mobile,
                    name = name,
                    profileDescription = profileDescription,
                )
            )
            if (!response.isSuccess) {
                return Result.failure(Exception(response.responseDesc ?: "Gagal menyimpan profil"))
            }
            val latestProfileResponse = apiService.getBuyerProfile()
            if (latestProfileResponse.isSuccess) {
                val latestProfile = latestProfileResponse.data
                sessionManager.updateProfile(
                    name = latestProfile?.name.orEmpty().ifBlank { name },
                    email = latestProfile?.email.orEmpty().ifBlank { email },
                    avatarUrl = latestProfile?.imageId.orEmpty(),
                )
            } else {
                sessionManager.updateProfile(
                    name = name,
                    email = email,
                )
            }
            Result.success(Unit)
        } catch (e: HttpException) {
            Result.failure(Exception(e.parseMessage()))
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    override suspend fun changePassword(
        oldPassword: String,
        newPassword: String,
    ): Result<Unit> {
        return try {
            val response = apiService.changePassword(
                ChangePasswordRequest(
                    newPassword = newPassword,
                    oldPassword = oldPassword,
                )
            )
            if (!response.isSuccess) {
                return Result.failure(Exception(response.responseDesc ?: "Gagal mengubah kata sandi"))
            }
            Result.success(Unit)
        } catch (e: HttpException) {
            Result.failure(Exception(e.parseMessage()))
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    override suspend fun logout() {
        sessionManager.clearToken()
    }

    private fun copyUriToTempFile(uri: Uri): File {
        val extension = context.contentResolver.getType(uri)
            ?.let { MimeTypeMap.getSingleton().getExtensionFromMimeType(it) }
            ?: "jpg"
        val tempFile = File(context.cacheDir, "upload-${UUID.randomUUID()}.$extension")
        context.contentResolver.openInputStream(uri)?.use { input ->
            tempFile.outputStream().use { output -> input.copyTo(output) }
        } ?: throw IllegalStateException("Tidak dapat membaca file gambar")
        return tempFile
    }

    private fun HttpException.parseMessage(): String {
        return try {
            val body = response()?.errorBody()?.string() ?: return "Terjadi kesalahan"
            // Try to extract responseDesc from error body
            val match = Regex(""""responseDesc"\s*:\s*"([^"]+)"""").find(body)
            match?.groupValues?.get(1) ?: "Terjadi kesalahan (${code()})"
        } catch (_: Exception) {
            "Terjadi kesalahan (${code()})"
        }
    }
}
