import Lottie
import SwiftUI

private enum CallFilter: String, CaseIterable {
    case all = "All"
    case missed = "Missed"
}

struct CallsView: View {
    @StateObject private var viewModel = CallsViewModel()
    @State private var selectedFilter: CallFilter = .all
    @State private var showCallError = false
    @State private var callErrorMessage = ""

    private var filteredCalls: [CallLogEntry] {
        let calls = viewModel.recentCalls
        switch selectedFilter {
        case .all:
            return calls
        case .missed:
            return calls.filter(\.isMissed)
        }
    }

    private var hasAnyCalls: Bool {
        !viewModel.recentCalls.isEmpty
    }

    var body: some View {
        NavigationStack {
            Group {
                if filteredCalls.isEmpty {
                    emptyStateContent
                } else {
                    callListContent
                }
            }
            .background(RosettaColors.Adaptive.background)
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    Button {
                        withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
                            viewModel.isEditing.toggle()
                        }
                    } label: {
                        Text(viewModel.isEditing ? "Done" : "Edit")
                            .font(.system(size: 17, weight: .medium))
                            .foregroundStyle(RosettaColors.Adaptive.text)
                            .fixedSize(horizontal: true, vertical: false)
                            .frame(height: 44)
                            .padding(.horizontal, 12)
                    }
                    .buttonStyle(.plain)
                    .glassCapsule()
                }

                ToolbarItem(placement: .principal) {
                    filterPicker
                }
            }
            .toolbarBackground(.hidden, for: .navigationBar)
            .task {
                viewModel.reload()
            }
            .overlay(alignment: .topLeading) {
                CallsDataObserver(viewModel: viewModel)
            }
            .alert("Call Error", isPresented: $showCallError) {
                Button("OK", role: .cancel) {}
            } message: {
                Text(callErrorMessage)
            }
        }
    }

    private func startCall(to call: CallLogEntry) {
        guard !call.opponentKey.isEmpty else { return }
        let result = CallManager.shared.startOutgoingCall(
            toPublicKey: call.opponentKey,
            title: call.name,
            username: ""
        )
        if result == .alreadyInCall {
            callErrorMessage = "You're already in a call"
            showCallError = true
        }
    }

    private func openChat(for call: CallLogEntry) {
        guard !call.opponentKey.isEmpty else { return }
        let route: ChatRoute
        if let dialog = DialogRepository.shared.dialogs[call.opponentKey] {
            route = ChatRoute(dialog: dialog)
        } else {
            route = ChatRoute(
                publicKey: call.opponentKey,
                title: call.name,
                username: "",
                verified: 0
            )
        }
        NotificationCenter.default.post(
            name: .openChatFromNotification,
            object: route
        )
    }

    private func deleteCall(_ call: CallLogEntry) {
        viewModel.deleteCallEntry(call)
    }

    private func deleteFilteredCalls() {
        let toDelete = filteredCalls
        viewModel.deleteCalls(toDelete)
    }
}

private extension CallsView {
    var emptyStateContent: some View {
        VStack(spacing: 0) {
            Spacer()

            if hasAnyCalls {
                Image(systemName: "phone.badge.exclamationmark")
                    .font(.system(size: 48, weight: .medium))
                    .foregroundStyle(RosettaColors.Adaptive.textSecondary.opacity(0.7))
                    .padding(.bottom, 18)

                Text("No \(selectedFilter.rawValue.lowercased()) calls yet.")
                    .font(.system(size: 16))
                    .foregroundStyle(RosettaColors.Adaptive.textSecondary)
                    .multilineTextAlignment(.center)
            } else {
                LottieView(
                    animationName: "phone_duck",
                    loopMode: .playOnce,
                    animationSpeed: 1.0
                )
                .frame(width: 200, height: 200)

                Spacer().frame(height: 24)

                Text("Your recent voice and video calls will\nappear here.")
                    .font(.system(size: 15))
                    .foregroundStyle(RosettaColors.Adaptive.textSecondary)
                    .multilineTextAlignment(.center)

            }

            Spacer()
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .offset(y: hasAnyCalls ? -10 : -40)
    }
}

private extension CallsView {
    var callListContent: some View {
        ScrollView {
            VStack(spacing: 0) {
                recentSection
                    .padding(.top, 8)
            }
            .padding(.bottom, 100)
        }
        .scrollContentBackground(.hidden)
    }

    var deleteAllButton: some View {
        Button(role: .destructive) {
            deleteFilteredCalls()
        } label: {
            Text("Delete All")
                .font(.system(size: 17))
                .foregroundStyle(RosettaColors.error)
        }
        .buttonStyle(.plain)
        .padding(.horizontal, 16)
        .padding(.top, 16)
    }
}

private extension CallsView {
    var filterPicker: some View {
        HStack(spacing: 0) {
            ForEach(CallFilter.allCases, id: \.self) { filter in
                Button {
                    withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
                        selectedFilter = filter
                    }
                } label: {
                    Text(filter.rawValue)
                        .font(.system(size: 14, weight: selectedFilter == filter ? .semibold : .regular))
                        .foregroundStyle(
                            selectedFilter == filter
                                ? RosettaColors.Adaptive.text
                                : RosettaColors.Adaptive.textSecondary
                        )
                        .padding(.horizontal, 12)
                        .frame(height: 32)
                        .background {
                            if selectedFilter == filter {
                                Capsule()
                                    .fill(RosettaColors.Adaptive.searchBarFill)
                            }
                        }
                        .contentShape(Capsule())
                }
                .buttonStyle(.plain)
            }
        }
        .padding(4)
        .glassCapsule()
    }
}

private extension CallsView {
    var recentSection: some View {
        VStack(alignment: .leading, spacing: 6) {
            Text("RECENT CALLS")
                .font(.system(size: 13, weight: .regular))
                .foregroundStyle(RosettaColors.Adaptive.textSecondary)
                .padding(.leading, 16)

            VStack(spacing: 0) {
                let calls = filteredCalls
                ForEach(Array(calls.enumerated()), id: \.element.id) { index, call in
                    callRow(call)

                    if index < calls.count - 1 {
                        Divider()
                            .background(RosettaColors.Adaptive.divider)
                            .padding(.leading, 88)
                    }
                }
            }

            if viewModel.isEditing {
                deleteAllButton
            }
        }
    }

    func callRow(_ call: CallLogEntry) -> some View {
        HStack(spacing: 10) {
            if viewModel.isEditing {
                Button {
                    deleteCall(call)
                } label: {
                    Image(systemName: "minus.circle.fill")
                        .font(.system(size: 22))
                        .foregroundStyle(RosettaColors.error)
                }
                .buttonStyle(.plain)
                .transition(.move(edge: .leading).combined(with: .opacity))
            }

            Button {
                startCall(to: call)
            } label: {
                HStack(spacing: 10) {
                    Image(systemName: call.direction.directionIcon)
                        .font(.system(size: 13))
                        .foregroundStyle(call.direction.directionColor)
                        .frame(width: 18)

                    CallRowAvatar(
                        opponentKey: call.opponentKey,
                        initials: call.initials,
                        colorIndex: call.colorIndex
                    )

                    VStack(alignment: .leading, spacing: 2) {
                        Text(call.name)
                            .font(.system(size: 17, weight: .semibold))
                            .foregroundStyle(call.isMissed ? RosettaColors.error : RosettaColors.Adaptive.text)
                            .lineLimit(1)

                        Text(call.subtitleText)
                            .font(.system(size: 14))
                            .foregroundStyle(RosettaColors.Adaptive.textSecondary)
                            .lineLimit(1)
                    }

                    Spacer()

                    Text(call.dateText)
                        .font(.system(size: 15))
                        .foregroundStyle(RosettaColors.Adaptive.textSecondary)
                }
                .contentShape(Rectangle())
            }
            .buttonStyle(.plain)

            Button {
                openChat(for: call)
            } label: {
                Image(systemName: "info.circle")
                    .font(.system(size: 22))
                    .foregroundStyle(RosettaColors.primaryBlue)
            }
            .buttonStyle(.plain)
        }
        .padding(.horizontal, 16)
        .padding(.vertical, 10)
        .contentShape(Rectangle())
        .accessibilityElement(children: .combine)
        .accessibilityLabel("\(call.name), \(call.subtitleText), \(call.dateText)")
    }
}

private extension CallLogDirection {
    var directionIcon: String {
        switch self {
        case .outgoing, .rejected:
            return "phone.arrow.up.right"
        case .incoming, .missed:
            return "phone.arrow.down.left"
        }
    }

    var directionColor: Color {
        switch self {
        case .outgoing, .incoming:
            return RosettaColors.success
        case .missed, .rejected:
            return RosettaColors.error
        }
    }
}

private struct CallsDataObserver: View {
    @ObservedObject var viewModel: CallsViewModel
    @ObservedObject private var callManager = CallManager.shared

    private var reloadToken: String {
        let dialogs = DialogRepository.shared.sortedDialogs
        let phaseTag = callManager.uiState.phase.rawValue

        guard !dialogs.isEmpty else { return "empty|\(phaseTag)" }

        let dialogPart = dialogs
            .map {
                "\($0.opponentKey):\($0.lastMessageTimestamp):\($0.lastMessageFromMe ? 1 : 0):\($0.lastMessageDelivered.rawValue):\($0.lastMessageRead ? 1 : 0)"
            }
            .joined(separator: "|")
        return "\(dialogPart)|\(phaseTag)"
    }

    var body: some View {
        Color.clear
            .frame(width: 0, height: 0)
            .allowsHitTesting(false)
            .task(id: reloadToken) {
                // Small delay when call just ended — allows sendCallAttachment Task to complete
                if callManager.uiState.phase == .idle {
                    try? await Task.sleep(for: .milliseconds(300))
                }
                viewModel.reload()
            }
    }
}

// MARK: - Call Row Avatar (observation-isolated)

/// Isolated child view so that `AvatarRepository.shared.avatarVersion` observation
/// does NOT propagate to the entire call list.
private struct CallRowAvatar: View {
    let opponentKey: String
    let initials: String
    let colorIndex: Int

    var body: some View {
        let _ = AvatarRepository.shared.avatarVersion
        AvatarView(
            initials: initials,
            colorIndex: colorIndex,
            size: 44,
            image: AvatarRepository.shared.loadAvatar(publicKey: opponentKey)
        )
    }
}

#Preview("Empty State") {
    CallsView()
        .preferredColorScheme(.dark)
}
