// ── Survey Client ────────────────────────────────────────
// In-app survey client for iOS/watchOS/macOS.
// Part of ByteLystPlatformSDK.

import Foundation

/// Survey question types.
public enum BLSurveyQuestionType: String, Codable, Sendable {
    case singleChoice = "single_choice"
    case multipleChoice = "multiple_choice"
    case rating = "rating"
    case nps = "nps"
    case textShort = "text_short"
    case textLong = "text_long"
    case dropdown = "dropdown"
    case scale = "scale"
    case ranking = "ranking"
}

/// Survey status.
public enum BLSurveyStatus: String, Codable, Sendable {
    case draft = "draft"
    case active = "active"
    case paused = "paused"
    case closed = "closed"
}

/// Represents a survey question option.
public struct BLSurveyOption: Codable, Sendable, Identifiable {
    public let id: String
    public let text: String
    public let emoji: String?
}

/// Represents a survey question.
public struct BLSurveyQuestion: Codable, Sendable, Identifiable {
    public let id: String
    public let type: BLSurveyQuestionType
    public let text: String
    public let description: String?
    public let required: Bool
    public let options: [BLSurveyOption]?
    public let minLength: Int?
    public let maxLength: Int?
    public let minValue: Int?
    public let maxValue: Int?
}

/// Represents an active survey for display.
public struct BLActiveSurvey: Codable, Sendable, Identifiable {
    public let id: String
    public let title: String
    public let description: String?
    public let questions: [BLSurveyQuestion]
    public let incentive: BLSurveyIncentive?
    public let displayTrigger: BLSurveyTrigger
}

/// Survey incentive.
public struct BLSurveyIncentive: Codable, Sendable {
    public let type: String
    public let amount: Int
}

/// Survey display trigger.
public struct BLSurveyTrigger: Codable, Sendable {
    public let type: String
    public let seconds: Int?
    public let eventName: String?
    public let pagePattern: String?
}

/// Survey answer types.
public enum BLSurveyAnswer: Codable, Sendable {
    case singleChoice(optionId: String)
    case multipleChoice(optionIds: [String])
    case rating(value: Int)
    case nps(value: Int)
    case text(value: String)
    case ranking(rankedOptionIds: [String])
    
    public var type: String {
        switch self {
        case .singleChoice: return "single_choice"
        case .multipleChoice: return "multiple_choice"
        case .rating: return "rating"
        case .nps: return "nps"
        case .text: return "text"
        case .ranking: return "ranking"
        }
    }
}

/// Represents a survey response.
public struct BLSurveyResponse: Codable, Sendable {
    public let id: String
    public let surveyId: String
    public let userId: String
    public var answers: [String: BLSurveyAnswer]
    public var currentQuestionIndex: Int
    public let startedAt: String
    public var completedAt: String?
    public var isComplete: Bool
    public var incentiveClaimed: Bool
    public var incentiveClaimedAt: String?
    public let createdAt: String
    public let updatedAt: String
}

/// Survey client for managing in-app surveys.
@available(iOS 15.0, macOS 12.0, watchOS 8.0, *)
public class BLSurveyClient: ObservableObject {
    private let platformClient: BLPlatformClient
    private var pollTask: Task<Void, Never>?
    private var cachedResponses: [String: BLSurveyResponse] = [:]
    
    public init(platformClient: BLPlatformClient) {
        self.platformClient = platformClient
    }
    
    /// Get active survey for the current user (if any).
    public func getActiveSurvey() async throws -> BLActiveSurvey? {
        let request = try platformClient.buildRequest(path: "/surveys/active")
        let (data, response) = try await URLSession.shared.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse,
              httpResponse.statusCode == 200 else {
            throw BLPlatformError.requestFailed(String(data: data, encoding: .utf8) ?? "Unknown error")
        }
        
        let result = try JSONDecoder().decode(ActiveSurveyResponse.self, from: data)
        return result.survey
    }
    
    /// Start a survey session.
    public func startSurvey(surveyId: String) async throws -> BLSurveyResponse {
        let request = try platformClient.buildRequest(
            path: "/surveys/\(surveyId)/start",
            method: "POST"
        )
        let (data, response) = try await URLSession.shared.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse,
              httpResponse.statusCode == 200 else {
            throw BLPlatformError.requestFailed("Failed to start survey")
        }
        
        let result = try JSONDecoder().decode(StartSurveyResponse.self, from: data)
        
        // Build and cache the response
        let surveyResponse = BLSurveyResponse(
            id: result.responseId,
            surveyId: surveyId,
            userId: "", // Will be filled by server
            answers: result.answers,
            currentQuestionIndex: result.currentQuestionIndex,
            startedAt: result.startedAt,
            completedAt: nil,
            isComplete: false,
            incentiveClaimed: false,
            incentiveClaimedAt: nil,
            createdAt: result.startedAt,
            updatedAt: result.startedAt
        )
        
        cachedResponses[surveyId] = surveyResponse
        return surveyResponse
    }
    
    /// Submit an answer to a survey question.
    public func submitAnswer(
        surveyId: String,
        questionId: String,
        answer: BLSurveyAnswer
    ) async throws -> BLSurveyResponse {
        let request = try platformClient.buildRequest(
            path: "/surveys/\(surveyId)/response",
            method: "POST",
            body: [
                "questionId": questionId,
                "answer": [
                    "type": answer.type,
                    "value": encodeAnswerValue(answer)
                ]
            ]
        )
        
        let (data, response) = try await URLSession.shared.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse,
              httpResponse.statusCode == 200 else {
            throw BLPlatformError.requestFailed("Failed to submit answer")
        }
        
        let result = try JSONDecoder().decode(SubmitAnswerResponse.self, from: data)
        
        // Update cache
        if var cached = cachedResponses[surveyId] {
            cached.answers = result.answers
            cached.currentQuestionIndex = result.currentQuestionIndex
            cachedResponses[surveyId] = cached
        }
        
        // Return updated response
        return BLSurveyResponse(
            id: result.responseId,
            surveyId: surveyId,
            userId: "",
            answers: result.answers,
            currentQuestionIndex: result.currentQuestionIndex,
            startedAt: "",
            completedAt: nil,
            isComplete: false,
            incentiveClaimed: false,
            incentiveClaimedAt: nil,
            createdAt: "",
            updatedAt: Date().ISO8601Format()
        )
    }
    
    /// Complete a survey.
    public func completeSurvey(surveyId: String) async throws -> SurveyCompletionResult {
        let request = try platformClient.buildRequest(
            path: "/surveys/\(surveyId)/complete",
            method: "POST"
        )
        let (data, response) = try await URLSession.shared.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse,
              httpResponse.statusCode == 200 else {
            throw BLPlatformError.requestFailed("Failed to complete survey")
        }
        
        let result = try JSONDecoder().decode(SurveyCompletionResult.self, from: data)
        
        // Clear cache on completion
        cachedResponses.removeValue(forKey: surveyId)
        
        return result
    }
    
    /// Dismiss a survey (won't show again).
    public func dismissSurvey(surveyId: String) async throws {
        let request = try platformClient.buildRequest(
            path: "/surveys/\(surveyId)/dismiss",
            method: "POST"
        )
        let (_, response) = try await URLSession.shared.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse,
              httpResponse.statusCode == 200 else {
            throw BLPlatformError.requestFailed("Failed to dismiss survey")
        }
        
        // Clear cache
        cachedResponses.removeValue(forKey: surveyId)
    }
    
    /// Get cached response for a survey.
    public func getCachedResponse(surveyId: String) -> BLSurveyResponse? {
        return cachedResponses[surveyId]
    }
    
    /// Start polling for eligible surveys.
    public func startPolling(
        interval: TimeInterval = 60,
        onUpdate: @escaping (BLActiveSurvey?) -> Void
    ) {
        stopPolling()
        
        pollTask = Task {
            while !Task.isCancelled {
                do {
                    let survey = try await getActiveSurvey()
                    onUpdate(survey)
                } catch {
                    // Silently ignore polling errors
                }
                
                try? await Task.sleep(nanoseconds: UInt64(interval * 1_000_000_000))
            }
        }
    }
    
    /// Stop polling for surveys.
    public func stopPolling() {
        pollTask?.cancel()
        pollTask = nil
    }
}

// MARK: - Survey Completion Result

public struct SurveyCompletionResult: Codable, Sendable {
    public let success: Bool
    public let timeSpentSeconds: Int
    public let incentiveClaimed: Bool
}

// MARK: - Response Types

private struct ActiveSurveyResponse: Codable {
    let survey: BLActiveSurvey?
}

private struct StartSurveyResponse: Codable {
    let responseId: String
    let startedAt: String
    let currentQuestionIndex: Int
    let answers: [String: BLSurveyAnswer]
}

private struct SubmitAnswerResponse: Codable {
    let responseId: String
    let currentQuestionIndex: Int
    let answers: [String: BLSurveyAnswer]
}

// MARK: - Helper Functions

private func encodeAnswerValue(_ answer: BLSurveyAnswer) -> AnyCodable {
    switch answer {
    case .singleChoice(let optionId):
        return AnyCodable(optionId)
    case .multipleChoice(let optionIds):
        return AnyCodable(optionIds)
    case .rating(let value), .nps(let value):
        return AnyCodable(value)
    case .text(let value):
        return AnyCodable(value)
    case .ranking(let rankedOptionIds):
        return AnyCodable(rankedOptionIds)
    }
}

/// Type-erased codable wrapper for encoding heterogeneous types.
private struct AnyCodable: Codable {
    private let value: Any
    
    init(_ value: Any) {
        self.value = value
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        if let string = value as? String {
            try container.encode(string)
        } else if let int = value as? Int {
            try container.encode(int)
        } else if let array = value as? [String] {
            try container.encode(array)
        } else {
            try container.encode(String(describing: value))
        }
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let string = try? container.decode(String.self) {
            value = string
        } else if let int = try? container.decode(Int.self) {
            value = int
        } else if let array = try? container.decode([String].self) {
            value = array
        } else {
            value = ""
        }
    }
}
