package com.bytelyst.platform

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.isActive
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import java.util.concurrent.TimeUnit

/**
 * Survey Client — In-app survey client for Android.
 * Part of ByteLystPlatformSDK.
 */
class BLSurveyClient(
    private val config: BLPlatformConfig,
    private val tokenProvider: () -> String? = { null },
) {
    private val httpClient = OkHttpClient.Builder()
        .connectTimeout(30, TimeUnit.SECONDS)
        .readTimeout(30, TimeUnit.SECONDS)
        .build()

    private val json = Json { ignoreUnknownKeys = true }
    private var pollingJob: Job? = null
    private val responseCache = mutableMapOf<String, SurveyResponse>()

    enum class QuestionType {
        SINGLE_CHOICE, MULTIPLE_CHOICE, RATING, NPS, TEXT_SHORT, TEXT_LONG, DROPDOWN, SCALE, RANKING
    }

    @Serializable
    data class QuestionOption(
        val id: String,
        val text: String,
        val emoji: String? = null,
    )

    @Serializable
    data class Question(
        val id: String,
        val type: String,
        val text: String,
        val description: String? = null,
        val required: Boolean,
        val options: List<QuestionOption>? = null,
        val minLength: Int? = null,
        val maxLength: Int? = null,
        val minValue: Int? = null,
        val maxValue: Int? = null,
    )

    @Serializable
    data class SurveyIncentive(
        val type: String,
        val amount: Int,
    )

    @Serializable
    data class SurveyTrigger(
        val type: String,
        val seconds: Int? = null,
        val eventName: String? = null,
        val pagePattern: String? = null,
    )

    @Serializable
    data class ActiveSurvey(
        val id: String,
        val title: String,
        val description: String? = null,
        val questions: List<Question>,
        val incentive: SurveyIncentive? = null,
        val displayTrigger: SurveyTrigger,
    )

    @Serializable
    data class SurveyAnswer(
        val type: String,
        val value: JsonObject,
    )

    @Serializable
    data class SurveyResponse(
        val id: String,
        val surveyId: String,
        val userId: String,
        val answers: Map<String, SurveyAnswer>,
        val currentQuestionIndex: Int,
        val startedAt: String,
        val completedAt: String? = null,
        val isComplete: Boolean,
        val incentiveClaimed: Boolean,
        val incentiveClaimedAt: String? = null,
        val createdAt: String,
        val updatedAt: String,
    )

    @Serializable
    private data class ActiveSurveyResponse(
        val survey: ActiveSurvey?,
    )

    @Serializable
    private data class StartSurveyResponse(
        val responseId: String,
        val startedAt: String,
        val currentQuestionIndex: Int,
        val answers: Map<String, SurveyAnswer>,
    )

    @Serializable
    private data class SubmitAnswerResponse(
        val responseId: String,
        val currentQuestionIndex: Int,
        val answers: Map<String, SurveyAnswer>,
    )

    @Serializable
    data class SurveyCompletionResult(
        val success: Boolean,
        val timeSpentSeconds: Int,
        val incentiveClaimed: Boolean,
    )

    /**
     * Get active survey for the current user (if any).
     */
    suspend fun getActiveSurvey(): Result<ActiveSurvey?> = withContext(Dispatchers.IO) {
        try {
            val request = buildRequest(path = "/surveys/active")
            val response = httpClient.newCall(request).execute()

            if (!response.isSuccessful) {
                return@withContext Result.failure(Exception("HTTP ${response.code}"))
            }

            val body = response.body?.string() ?: return@withContext Result.failure(Exception("Empty response"))
            val result = json.decodeFromString(ActiveSurveyResponse.serializer(), body)
            Result.success(result.survey)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    /**
     * Start a survey session.
     */
    suspend fun startSurvey(surveyId: String): Result<SurveyResponse> = withContext(Dispatchers.IO) {
        try {
            val request = buildRequest(
                path = "/surveys/$surveyId/start",
                method = "POST"
            )
            val response = httpClient.newCall(request).execute()

            if (!response.isSuccessful) {
                return@withContext Result.failure(Exception("HTTP ${response.code}"))
            }

            val body = response.body?.string() ?: return@withContext Result.failure(Exception("Empty response"))
            val result = json.decodeFromString(StartSurveyResponse.serializer(), body)

            val surveyResponse = SurveyResponse(
                id = result.responseId,
                surveyId = surveyId,
                userId = "",
                answers = result.answers,
                currentQuestionIndex = result.currentQuestionIndex,
                startedAt = result.startedAt,
                completedAt = null,
                isComplete = false,
                incentiveClaimed = false,
                incentiveClaimedAt = null,
                createdAt = result.startedAt,
                updatedAt = result.startedAt,
            )

            // Cache the response
            responseCache[surveyId] = surveyResponse
            Result.success(surveyResponse)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    /**
     * Submit an answer to a survey question.
     */
    suspend fun submitAnswer(
        surveyId: String,
        questionId: String,
        answer: SurveyAnswer,
    ): Result<SurveyResponse> = withContext(Dispatchers.IO) {
        try {
            val body = json.encodeToString(
                SubmitAnswerRequest.serializer(),
                SubmitAnswerRequest(questionId, answer)
            )

            val request = buildRequest(
                path = "/surveys/$surveyId/response",
                method = "POST",
                body = body
            )
            val response = httpClient.newCall(request).execute()

            if (!response.isSuccessful) {
                return@withContext Result.failure(Exception("HTTP ${response.code}"))
            }

            val responseBody = response.body?.string() ?: return@withContext Result.failure(Exception("Empty response"))
            val result = json.decodeFromString(SubmitAnswerResponse.serializer(), responseBody)

            // Update cache
            val cached = responseCache[surveyId]
            if (cached != null) {
                val updated = cached.copy(
                    answers = result.answers,
                    currentQuestionIndex = result.currentQuestionIndex,
                )
                responseCache[surveyId] = updated
            }

            Result.success(
                SurveyResponse(
                    id = result.responseId,
                    surveyId = surveyId,
                    userId = "",
                    answers = result.answers,
                    currentQuestionIndex = result.currentQuestionIndex,
                    startedAt = "",
                    completedAt = null,
                    isComplete = false,
                    incentiveClaimed = false,
                    incentiveClaimedAt = null,
                    createdAt = "",
                    updatedAt = java.time.Instant.now().toString(),
                )
            )
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    /**
     * Complete a survey.
     */
    suspend fun completeSurvey(surveyId: String): Result<SurveyCompletionResult> = withContext(Dispatchers.IO) {
        try {
            val request = buildRequest(
                path = "/surveys/$surveyId/complete",
                method = "POST"
            )
            val response = httpClient.newCall(request).execute()

            if (!response.isSuccessful) {
                return@withContext Result.failure(Exception("HTTP ${response.code}"))
            }

            val body = response.body?.string() ?: return@withContext Result.failure(Exception("Empty response"))
            val result = json.decodeFromString(SurveyCompletionResult.serializer(), body)

            // Clear cache on completion
            responseCache.remove(surveyId)
            Result.success(result)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    /**
     * Dismiss a survey (won't show again).
     */
    suspend fun dismissSurvey(surveyId: String): Result<Unit> = withContext(Dispatchers.IO) {
        try {
            val request = buildRequest(
                path = "/surveys/$surveyId/dismiss",
                method = "POST"
            )
            val response = httpClient.newCall(request).execute()

            if (!response.isSuccessful) {
                return@withContext Result.failure(Exception("HTTP ${response.code}"))
            }

            // Clear cache
            responseCache.remove(surveyId)
            Result.success(Unit)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    /**
     * Get cached response for a survey.
     */
    fun getCachedResponse(surveyId: String): SurveyResponse? {
        return responseCache[surveyId]
    }

    /**
     * Start polling for eligible surveys.
     */
    fun startPolling(
        intervalMs: Long = 60000L,
        onUpdate: (ActiveSurvey?) -> Unit,
    ) {
        stopPolling()
        pollingJob = CoroutineScope(Dispatchers.IO).launch {
            while (isActive) {
                getActiveSurvey()
                    .onSuccess { survey -> onUpdate(survey) }
                    .onFailure { /* Silently ignore polling errors */ }
                delay(intervalMs)
            }
        }
    }

    /**
     * Stop polling for surveys.
     */
    fun stopPolling() {
        pollingJob?.cancel()
        pollingJob = null
    }

    @Serializable
    private data class SubmitAnswerRequest(
        val questionId: String,
        val answer: SurveyAnswer,
    )

    private fun buildRequest(
        path: String,
        method: String = "GET",
        body: String? = null,
    ): Request {
        val url = "${config.baseUrl}$path"
        val token = tokenProvider() ?: ""

        val builder = Request.Builder()
            .url(url)
            .header("Authorization", "Bearer $token")
            .header("x-product-id", config.productId)
            .header("x-platform", "android")
            .header("x-app-version", config.appVersion)
            .header("x-os-version", config.osVersion)

        if (body != null) {
            builder.method(method, body.toRequestBody("application/json".toMediaType()))
        } else if (method != "GET") {
            builder.method(method, "".toRequestBody(null))
        }

        return builder.build()
    }
}
