import Foundation
import PhotosUI
import SwiftUI

struct ChatMessage: Identifiable {
    let id = UUID()
    let role: String // "user" or "assistant"
    let content: String
    let timestamp = Date()
}

@Observable
final class AssistantViewModel {
    var messages: [ChatMessage] = []
    var inputText = ""
    var isLoading = false
    var error: String?
    var entryDate = Date()

    // State from server — stored as raw JSON, never decoded with Codable
    private var serverState: Any?

    // Draft data — parsed manually from raw dict
    var currentDraft: FitnessDraft?
    var currentDrafts: [FitnessDraft] = []
    var applied = false

    // Photo
    var selectedPhoto: PhotosPickerItem?
    var imageDataUrl: String?

    private let api = FitnessAPI()

    var dateString: String {
        entryDate.apiDateString
    }

    // MARK: - Send Message

    func send() async {
        let text = inputText.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !text.isEmpty else { return }

        messages.append(ChatMessage(role: "user", content: text))
        inputText = ""
        isLoading = true
        error = nil
        applied = false

        await doSend(action: "chat")
    }

    func applyDraft() async {
        isLoading = true
        error = nil
        await doSend(action: "apply")
    }

    func applyAllDrafts() async {
        isLoading = true
        error = nil
        await doSend(action: "apply")
    }

    private func doSend(action: String) async {
        do {
            // Build message array for API
            let msgArray: [[String: String]] = messages.map { msg in
                ["role": msg.role, "content": msg.content]
            }

            // Build request as raw dictionary
            var requestDict: [String: Any] = [
                "messages": msgArray,
                "action": action,
                "entryDate": dateString,
                "allowBrain": false,
            ]

            if let state = serverState {
                requestDict["state"] = state
            }

            if let draft = currentDraft {
                requestDict["draft"] = draftToDict(draft)
            }

            if !currentDrafts.isEmpty {
                requestDict["drafts"] = currentDrafts.map { draftToDict($0) }
            }

            if let imageUrl = imageDataUrl {
                requestDict["imageDataUrl"] = imageUrl
                imageDataUrl = nil
            }

            let requestData = try JSONSerialization.data(withJSONObject: requestDict)
            let responseData = try await api.sendAssistantMessage(data: requestData)

            // Parse response as raw dict
            guard let responseDict = try JSONSerialization.jsonObject(with: responseData) as? [String: Any] else {
                error = "Invalid response from server"
                isLoading = false
                return
            }

            // Extract display fields
            if let reply = responseDict["reply"] as? String, !reply.isEmpty {
                messages.append(ChatMessage(role: "assistant", content: reply))
            }

            // Store state as raw
            if let state = responseDict["state"] {
                serverState = state
            }

            // Parse draft
            if let draftDict = responseDict["draft"] as? [String: Any] {
                currentDraft = FitnessDraft(from: draftDict)
            }

            // Parse drafts array
            if let draftsArray = responseDict["drafts"] as? [[String: Any]] {
                currentDrafts = draftsArray.map { FitnessDraft(from: $0) }
                if currentDraft == nil && !currentDrafts.isEmpty {
                    currentDraft = currentDrafts.first
                }
            }

            // Check applied
            if let appliedValue = responseDict["applied"] as? Bool {
                applied = appliedValue
                if appliedValue {
                    currentDraft = nil
                    currentDrafts = []
                }
            }

            if let errorMsg = responseDict["error"] as? String {
                error = errorMsg
            }

        } catch {
            self.error = error.localizedDescription
        }

        isLoading = false
    }

    // MARK: - Photo handling

    func handlePhotoSelection() async {
        guard let item = selectedPhoto else { return }
        guard let data = try? await item.loadTransferable(type: Data.self) else { return }

        // Resize for upload
        guard let image = UIImage(data: data) else { return }
        let maxDim: CGFloat = 800
        let scale = min(maxDim / image.size.width, maxDim / image.size.height, 1.0)
        let newSize = CGSize(width: image.size.width * scale, height: image.size.height * scale)
        let renderer = UIGraphicsImageRenderer(size: newSize)
        let resized = renderer.image { _ in
            image.draw(in: CGRect(origin: .zero, size: newSize))
        }

        if let jpegData = resized.jpegData(compressionQuality: 0.7) {
            let base64 = jpegData.base64EncodedString()
            imageDataUrl = "data:image/jpeg;base64,\(base64)"
            messages.append(ChatMessage(role: "user", content: "[Photo attached]"))
            await doSend(action: "chat")
        }

        selectedPhoto = nil
    }

    // MARK: - Helpers

    private func draftToDict(_ draft: FitnessDraft) -> [String: Any] {
        [
            "food_name": draft.foodName,
            "meal_type": draft.mealType,
            "entry_date": draft.entryDate,
            "quantity": draft.quantity,
            "unit": draft.unit,
            "calories": draft.calories,
            "protein": draft.protein,
            "carbs": draft.carbs,
            "fat": draft.fat,
            "sugar": draft.sugar,
            "fiber": draft.fiber,
            "note": draft.note,
            "default_serving_label": draft.defaultServingLabel,
        ]
    }

    func reset() {
        messages = []
        inputText = ""
        serverState = nil
        currentDraft = nil
        currentDrafts = []
        applied = false
        error = nil
        imageDataUrl = nil
    }
}
