//
//  MessagesView.swift
//  lichunWebsocket
//
//  Messages list view - displays all active conversations
//

import SwiftUI

/// Wrapper to reliably trigger fullScreenCover with embedded character ID
struct MessagesChatPresentation: Identifiable {
    let id: String  // character ID
}

struct MessagesView: View {
    @EnvironmentObject var webSocketService: WebSocketService
    @EnvironmentObject var appViewModel: AppViewModel

    // Use Identifiable wrapper for reliable fullScreenCover presentation
    @State private var chatPresentation: MessagesChatPresentation?
    @State private var appearedMessages: Set<String> = []
    @State private var searchText: String = ""
    @State private var selectedFilter: MessageFilter = .all
    @State private var cachedCharacters: [Person] = []
    @State private var lastUpdateTime: Date = Date()

    // MARK: - Computed Properties

    /// Filtered list of characters who have active conversations (cached)
    private var charactersWithMessages: [Person] {
        return cachedCharacters
    }

    private var conversationChangeSignature: String {
        webSocketService.player.activeConversations
            .map { conversation in
                [
                    conversation.id,
                    conversation.character ?? "",
                    "\(conversation.conversation.count)",
                    "\(conversation.unread)",
                    conversation.mostRecentMessage?.id ?? ""
                ].joined(separator: ":")
            }
            .joined(separator: "|")
    }

    /// Update the cached characters list
    private func updateCache() {
        var filtered = webSocketService.player.r.filter { person in
            webSocketService.player.activeConversations.contains(where: {
                $0.character == person.id && !$0.conversation.isEmpty
            })
        }

        // Apply search filter
        if !searchText.isEmpty {
            filtered = filtered.filter { person in
                let fullName = "\(person.firstname) \(person.lastname)".lowercased()
                return fullName.contains(searchText.lowercased())
            }
        }

        // Apply relationship filter
        if selectedFilter != .all {
            filtered = filtered.filter { person in
                filterMatches(person: person, filter: selectedFilter)
            }
        }

        // Sort: unread conversations first, then by most recent message
        // Uses mostRecentMessage which handles ISO8601 datetime parsing
        cachedCharacters = filtered.sorted { person1, person2 in
            let conv1 = webSocketService.player.activeConversations.first(where: { $0.character == person1.id })
            let conv2 = webSocketService.player.activeConversations.first(where: { $0.character == person2.id })

            // Unread conversations come first
            let unread1 = conv1?.unread ?? false
            let unread2 = conv2?.unread ?? false
            if unread1 != unread2 {
                return unread1
            }

            // Then sort by most recent message datetime
            // mostRecentMessage already handles datetime parsing internally
            guard let lastMessage1 = conv1?.mostRecentMessage,
                  let date1 = MessagesView.parseDateTime(lastMessage1.datetime) else { return false }

            guard let lastMessage2 = conv2?.mostRecentMessage,
                  let date2 = MessagesView.parseDateTime(lastMessage2.datetime) else { return true }

            return date1 > date2
        }

        lastUpdateTime = Date()
    }

    /// Parse datetime string that may be in ISO8601 or custom format
    private static func parseDateTime(_ dateString: String) -> Date? {
        // Try ISO8601 format first (server sends this)
        let iso8601Formatter = ISO8601DateFormatter()
        iso8601Formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
        if let date = iso8601Formatter.date(from: dateString) {
            return date
        }

        // Try ISO8601 without fractional seconds
        iso8601Formatter.formatOptions = [.withInternetDateTime]
        if let date = iso8601Formatter.date(from: dateString) {
            return date
        }

        // Fallback to custom format (legacy)
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSSSSS"
        return dateFormatter.date(from: dateString)
    }

    /// Check if person matches filter
    private func filterMatches(person: Person, filter: MessageFilter) -> Bool {
        let relationships = person.relationships.map { $0.lowercased() }

        switch filter {
        case .all:
            return true
        case .friends:
            return relationships.contains { $0.contains("friend") }
        case .family:
            return relationships.contains { $0.contains("family") || $0.contains("parent") || $0.contains("sibling") }
        case .romantic:
            return relationships.contains { $0.contains("love") || $0.contains("romantic") }
        }
    }

    // MARK: - Body

    var body: some View {
        ZStack {
            // Background
            AppColors.background
                .ignoresSafeArea()

            if charactersWithMessages.isEmpty && searchText.isEmpty && selectedFilter == .all {
                // Empty state (no conversations at all)
                MessagesEmptyState()
            } else if charactersWithMessages.isEmpty {
                // No results for search/filter
                noResultsView
            } else {
                // Messages list
                ScrollView(showsIndicators: false) {
                    VStack(spacing: AppSpacing.md) {
                        // Header with search and filters
                        MessagesHeaderCard(
                            searchText: $searchText,
                            selectedFilter: $selectedFilter,
                            totalMessageCount: webSocketService.player.activeConversations.filter { !$0.conversation.isEmpty }.count
                        )
                        .padding(.horizontal, AppSpacing.md)
                        .padding(.top, AppSpacing.md)

                        // Message rows
                        LazyVStack(spacing: AppSpacing.md) {
                            ForEach(Array(charactersWithMessages.enumerated()), id: \.element.id) { (index: Int, person) in
                                Button(action: {
                                    // Haptic feedback
                                    let generator = UIImpactFeedbackGenerator(style: .light)
                                    generator.impactOccurred()

                                    // Use Identifiable wrapper for reliable fullScreenCover
                                    chatPresentation = MessagesChatPresentation(id: person.id)
                                }) {
                                    MessageRowCard(person: person)
                                        .padding(.horizontal, AppSpacing.md)
                                }
                                .buttonStyle(PlainButtonStyle())
                                .opacity(appearedMessages.contains(person.id) ? 1 : 0)
                                .offset(y: appearedMessages.contains(person.id) ? 0 : 6)
                                .onAppear {
                                    withAnimation(.easeOut(duration: 0.12).delay(Double(index) * 0.02)) {
                                        _ = appearedMessages.insert(person.id)
                                    }
                                }
                            }
                        }
                        .padding(.bottom, AppSpacing.xl)
                    }
                }
            }
        }
        .fullScreenCover(item: $chatPresentation) { presentation in
            ChatView(characterID: presentation.id)
                .environmentObject(webSocketService)
                .environmentObject(appViewModel)
                .id(presentation.id)
        }
        .onAppear {
            AnalyticsManager.shared.trackScreenView("messages", screenClass: "MessagesView")
            updateCache()
        }
        .onChange(of: webSocketService.pendingOpenConversation) { characterId in
            if let characterId = characterId {
                // Clear immediately to prevent re-entry if view is dismissed quickly
                webSocketService.pendingOpenConversation = nil
                // Small delay to let the sheet finish appearing
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
                    chatPresentation = MessagesChatPresentation(id: characterId)
                }
            }
        }
        .onChange(of: conversationChangeSignature) { _ in
            updateCache()
        }
        .onChange(of: searchText) { _ in
            updateCache()
        }
        .onChange(of: selectedFilter) { _ in
            updateCache()
        }
    }

    // MARK: - No Results View

    private var noResultsView: some View {
        VStack(spacing: AppSpacing.lg) {
            // Cozy search icon
            ZStack {
                Circle()
                    .fill(
                        LinearGradient(
                            colors: [AppColors.info.opacity(0.15), AppColors.secondary.opacity(0.15)],
                            startPoint: .topLeading,
                            endPoint: .bottomTrailing
                        )
                    )
                    .frame(width: 90, height: 90)

                Image(systemName: "magnifyingglass")
                    .font(.system(size: 38))
                    .foregroundColor(AppColors.info)
            }

            Text("No conversations found")
                .font(.appHeadline)
                .foregroundColor(AppColors.primaryText)

            Text("Try adjusting your search or filters")
                .font(.appBody)
                .foregroundColor(AppColors.secondaryText)

            // Cozy button
            Button(action: {
                searchText = ""
                selectedFilter = .all

                // Haptic feedback
                let generator = UIImpactFeedbackGenerator(style: .light)
                generator.impactOccurred()
            }) {
                Text("Clear Filters")
                    .font(.appBodyBold)
                    .foregroundColor(.white)
                    .padding(.horizontal, AppSpacing.lg)
                    .padding(.vertical, AppSpacing.sm)
                    .background(
                        LinearGradient(
                            colors: [AppColors.primary, AppColors.accent],
                            startPoint: .leading,
                            endPoint: .trailing
                        )
                    )
                    .cornerRadius(16)
                    .shadow(
                        color: AppColors.primary.opacity(0.25),
                        radius: 8,
                        x: 0,
                        y: 4
                    )
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .padding(AppSpacing.xl)
    }
}

// MARK: - Preview

#Preview {
    MessagesPreviewContainer()
}

private struct MessagesPreviewContainer: View {
    @StateObject private var service = WebSocketService(urlSession: URLSession.shared, delegateQueue: OperationQueue())
    @StateObject private var appViewModel = AppViewModel()

    var body: some View {
        let _ = setupMockData()

        MessagesView()
            .environmentObject(service)
            .environmentObject(appViewModel)
    }

    private func setupMockData() {
        let person1 = Person()
        person1.id = "1"
        person1.firstname = "Emma"
        person1.lastname = "Chen"
        person1.image = "https://api.dicebear.com/7.x/avataaars/svg?seed=Emma"
        person1.affinity = 75
        person1.relationships = ["friend"]

        let person2 = Person()
        person2.id = "2"
        person2.firstname = "James"
        person2.lastname = "Wilson"
        person2.image = "https://api.dicebear.com/7.x/avataaars/svg?seed=James"
        person2.affinity = 85
        person2.relationships = ["love_interest"]

        service.player.r = [person1, person2]

        let conv1 = ConversationObj(id: "conv1", cType: "casual", character: "1", conversation: [], question: 0)
        let conv2 = ConversationObj(id: "conv2", cType: "casual", character: "2", conversation: [], question: 0)
        service.player.activeConversations = [conv1, conv2]
    }
}
