import SwiftUI
import Foundation

struct ContactsTab: View {
    @ObservedObject private var loginViewModel: LoginViewModel
    @State private var contacts: [Contact] = []
    @State private var isLoading = false
    @State private var loadError: String?
    @State private var pagingError: String?
    @State private var activeAlert: ContactsAlert?
    @State private var hasMore = true
    @State private var offset = 0
    @State private var creatingChatForContactId: UUID?
    @State private var pendingChatItem: PrivateChatListItem?
    @State private var isPendingChatActive = false
    @State private var contactAvatars: [UUID: AvatarInfo] = [:]
    @State private var avatarLoadedIds: Set<UUID> = []
    @State private var avatarLoadingIds: Set<UUID> = []
    @State private var contactToEdit: Contact?
    @State private var contactPendingBlock: Contact?
    @State private var contactPendingDelete: Contact?
    @State private var showBlockConfirmation = false
    @State private var showDeleteConfirmation = false
    @State private var blockingContactIds: Set<UUID> = []
    @State private var deletingContactIds: Set<UUID> = []

    private let contactsService = ContactsService()
    private let chatService = ChatService()
    private let profileService = ProfileService()
    private let blockedUsersService = BlockedUsersService()
    private let pageSize = 25

    private var currentUserId: String? {
        let identifier = loginViewModel.userId
        return identifier.isEmpty ? nil : identifier
    }

    init(viewModel: LoginViewModel) {
        self._loginViewModel = ObservedObject(wrappedValue: viewModel)
    }

    var body: some View {
        List {
            if isLoading && contacts.isEmpty {
                loadingState
            }

            if let loadError, contacts.isEmpty {
                errorState(loadError)
            } else if contacts.isEmpty {
                emptyState
            } else {
                ForEach(Array(contacts.enumerated()), id: \.element.id) { index, contact in
                    Button {
                        openChat(for: contact)
                    } label: {
                        ContactRow(
                            contact: contact,
                            avatarInfo: contactAvatars[contact.id],
                            currentUserId: currentUserId,
                            isLoading: isRowBusy(contact)
                        )
                            .contentShape(Rectangle())
                    }
                    .buttonStyle(.plain)
                    .disabled(isRowBusy(contact))
                    .contextMenu {
                        Button {
                            handleContactAction(.edit, for: contact)
                        } label: {
                            Label(
                                NSLocalizedString("Изменить контакт", comment: "Contacts context action edit"),
                                systemImage: "square.and.pencil"
                            )
                        }
                        .disabled(contact.isDeleted)

                        Button {
                            handleContactAction(.block, for: contact)
                        } label: {
                            Label(
                                NSLocalizedString("Заблокировать контакт", comment: "Contacts context action block"),
                                systemImage: "hand.raised.fill"
                            )
                        }
                        .disabled(contact.isDeleted)

                        Button(role: .destructive) {
                            handleContactAction(.delete, for: contact)
                        } label: {
                            Label(
                                NSLocalizedString("Удалить контакт", comment: "Contacts context action delete"),
                                systemImage: "trash"
                            )
                        }
                    }
                    .listRowInsets(EdgeInsets(top: 0, leading: 12, bottom: 0, trailing: 12))
                    .onAppear {
                        loadAvatarIfNeeded(for: contact)
                        if index >= contacts.count - 5 {
                            Task {
                                await loadContacts(reset: false)
                            }
                        }
                    }
                }

                if isLoading && !contacts.isEmpty {
                    loadingState
                } else if let pagingError, !contacts.isEmpty {
                    pagingErrorState(pagingError)
                }
            }
        }
        .navigationTitle(NSLocalizedString("Контакты", comment: "Contacts tab title"))
        .background(Color(UIColor.systemBackground))
        .listStyle(.plain)
        .task {
            await loadContacts(reset: false)
        }
        .alert(item: $activeAlert) { alert in
            switch alert {
            case .error(_, let message):
                return Alert(
                    title: Text(NSLocalizedString("Ошибка", comment: "Contacts load error title")),
                    message: Text(message),
                    dismissButton: .default(Text(NSLocalizedString("OK", comment: "Common OK")))
                )
            case .info(_, let title, let message):
                return Alert(
                    title: Text(title),
                    message: Text(message),
                    dismissButton: .default(Text(NSLocalizedString("OK", comment: "Common OK")))
                )
            }
        }
        .sheet(item: $contactToEdit) { contact in
            NavigationView {
                ContactEditView(
                    contact: contactEditInfo(for: contact),
                    onContactDeleted: {
                        handleContactRemoved(contact.id)
                    },
                    onContactUpdated: { newName in
                        handleContactRenamed(contact.id, newName: newName)
                    }
                )
            }
        }
        .confirmationDialog(
            NSLocalizedString("Заблокировать контакт?", comment: "Contacts block confirmation title"),
            isPresented: $showBlockConfirmation,
            presenting: contactPendingBlock
        ) { contact in
            Button(NSLocalizedString("Заблокировать", comment: "Contacts block confirm action"), role: .destructive) {
                showBlockConfirmation = false
                contactPendingBlock = nil
                performBlockContact(contact)
            }
            Button(NSLocalizedString("Отмена", comment: "Common cancel"), role: .cancel) {
                showBlockConfirmation = false
                contactPendingBlock = nil
            }
        } message: { contact in
            Text(String(
                format: NSLocalizedString("Пользователь \"%1$@\" будет добавлен в чёрный список.", comment: "Contacts block confirmation message"),
                contact.displayName
            ))
        }
        .confirmationDialog(
            NSLocalizedString("Удалить контакт?", comment: "Contacts delete confirmation title"),
            isPresented: $showDeleteConfirmation,
            presenting: contactPendingDelete
        ) { contact in
            Button(NSLocalizedString("Удалить", comment: "Contacts delete confirm action"), role: .destructive) {
                showDeleteConfirmation = false
                contactPendingDelete = nil
                performDeleteContact(contact)
            }
            Button(NSLocalizedString("Отмена", comment: "Common cancel"), role: .cancel) {
                showDeleteConfirmation = false
                contactPendingDelete = nil
            }
        } message: { contact in
            Text(String(
                format: NSLocalizedString("Контакт \"%1$@\" будет удалён из списка.", comment: "Contacts delete confirmation message"),
                contact.displayName
            ))
        }
        .overlay(pendingChatNavigationLink)
    }

    private var loadingState: some View {
        HStack {
            Spacer()
            ProgressView()
                .progressViewStyle(CircularProgressViewStyle())
            Spacer()
        }
        .padding(.vertical, 18)
        .listRowInsets(EdgeInsets(top: 18, leading: 12, bottom: 18, trailing: 12))
        .listRowSeparator(.hidden)
    }

    private func errorState(_ message: String) -> some View {
        HStack(alignment: .center, spacing: 8) {
            Image(systemName: "exclamationmark.triangle.fill")
                .foregroundColor(.orange)
            Text(message)
                .font(.subheadline)
                .foregroundColor(.orange)
            Spacer()
            Button(action: { Task { await refreshContacts() } }) {
                Text(NSLocalizedString("Обновить", comment: "Contacts retry button"))
                    .font(.subheadline)
            }
        }
        .padding(.vertical, 10)
        .listRowInsets(EdgeInsets(top: 10, leading: 12, bottom: 10, trailing: 12))
        .listRowSeparator(.hidden)
    }

    private func pagingErrorState(_ message: String) -> some View {
        HStack(alignment: .center, spacing: 8) {
            Image(systemName: "exclamationmark.triangle.fill")
                .foregroundColor(.orange)
            Text(message)
                .font(.subheadline)
                .foregroundColor(.orange)
            Spacer()
            Button(action: { Task { await loadContacts(reset: false) } }) {
                Text(NSLocalizedString("Обновить", comment: "Contacts retry button"))
                    .font(.subheadline)
            }
        }
        .padding(.vertical, 10)
        .listRowInsets(EdgeInsets(top: 10, leading: 12, bottom: 10, trailing: 12))
        .listRowSeparator(.hidden)
    }

    private var emptyState: some View {
        VStack(spacing: 12) {
            Image(systemName: "person.crop.circle.badge.questionmark")
                .font(.system(size: 52))
                .foregroundColor(.secondary)
            Text(NSLocalizedString("Контактов пока нет", comment: "Contacts empty state title"))
                .font(.headline)
                .multilineTextAlignment(.center)
            Text(NSLocalizedString("Добавьте контакты, чтобы быстрее выходить на связь.", comment: "Contacts empty state subtitle"))
                .font(.subheadline)
                .foregroundColor(.secondary)
                .multilineTextAlignment(.center)
        }
        .frame(maxWidth: .infinity)
        .padding(.vertical, 28)
        .listRowInsets(EdgeInsets(top: 20, leading: 12, bottom: 20, trailing: 12))
        .listRowSeparator(.hidden)
    }

    @MainActor
    private func refreshContacts() async {
        hasMore = true
        offset = 0
        pagingError = nil
        loadError = nil
        contacts.removeAll()
        await loadContacts(reset: true)
    }

    @MainActor
    private func loadContacts(reset: Bool) async {
        if isLoading { return }
        if !reset && !hasMore { return }

        isLoading = true
        if offset == 0 {
            loadError = nil
        }
        pagingError = nil

        do {
            let payload = try await contactsService.fetchContacts(limit: pageSize, offset: offset)
            let newContacts = payload.items.map(Contact.init)
            if reset {
                contacts = newContacts
            } else {
                contacts.append(contentsOf: newContacts)
            }
            offset += newContacts.count
            hasMore = payload.hasMore
        } catch {
            let message = error.localizedDescription
            if contacts.isEmpty {
                loadError = message
            } else {
                pagingError = message
            }
            if AppConfig.DEBUG { print("[ContactsTab] load contacts failed: \(error)") }
        }

        isLoading = false
    }

    private func handleContactAction(_ action: ContactAction, for contact: Contact) {
        guard !isRowBusy(contact) else { return }
        switch action {
        case .edit:
            contactToEdit = contact
        case .block:
            contactPendingBlock = contact
            showBlockConfirmation = true
        case .delete:
            contactPendingDelete = contact
            showDeleteConfirmation = true
        }
    }

    private var pendingChatNavigationLink: some View {
        NavigationLink(
            destination: pendingChatDestination,
            isActive: Binding(
                get: { isPendingChatActive && pendingChatItem != nil },
                set: { newValue in
                    if !newValue {
                        isPendingChatActive = false
                        pendingChatItem = nil
                    }
                }
            )
        ) {
            EmptyView()
        }
        .hidden()
    }

    @ViewBuilder
    private var pendingChatDestination: some View {
        if let pendingChatItem {
            PrivateChatView(chat: pendingChatItem, currentUserId: currentUserId)
        } else {
            EmptyView()
        }
    }

    private func openChat(for contact: Contact) {
        guard creatingChatForContactId == nil else { return }

        creatingChatForContactId = contact.id

        chatService.createOrFindPrivateChat(targetUserId: contact.id.uuidString) { result in
            DispatchQueue.main.async {
                creatingChatForContactId = nil

                switch result {
                case .success(let data):
                    let chatItem = PrivateChatListItem(
                        chatId: data.chatId,
                        chatType: data.chatType,
                        chatData: chatProfile(for: contact),
                        lastMessage: nil,
                        createdAt: nil,
                        unreadCount: 0
                    )
                    pendingChatItem = chatItem
                    isPendingChatActive = true
                case .failure(let error):
                    activeAlert = .error(message: friendlyChatCreationMessage(for: error))
                }
            }
        }
    }

    private func chatProfile(for contact: Contact) -> ChatProfile {
        ChatProfile(
            userId: contact.id.uuidString,
            login: contact.login,
            fullName: contact.fullName,
            customName: contact.customName,
            createdAt: contact.createdAt,
            isOfficial: false
        )
    }

    private func friendlyChatCreationMessage(for error: Error) -> String {
        if let chatError = error as? ChatServiceError {
            return chatError.errorDescription ?? NSLocalizedString("Не удалось открыть чат.", comment: "Chat creation fallback")
        }

        if let networkError = error as? NetworkError {
            switch networkError {
            case .unauthorized:
                return NSLocalizedString("Сессия истекла. Войдите снова.", comment: "Chat creation unauthorized")
            case .invalidURL, .noResponse:
                return NSLocalizedString("Ошибка соединения с сервером.", comment: "Chat creation connection")
            case .network(let underlying):
                return String(format: NSLocalizedString("Ошибка сети: %@", comment: "Chat creation network error"), underlying.localizedDescription)
            case .server(let statusCode, let data):
                if let data {
                    let decoder = JSONDecoder()
                    decoder.keyDecodingStrategy = .convertFromSnakeCase
                    if let payload = try? decoder.decode(ErrorResponse.self, from: data) {
                        if let detail = payload.detail?.trimmingCharacters(in: .whitespacesAndNewlines), !detail.isEmpty {
                            return detail
                        }
                        if let message = payload.data?.message?.trimmingCharacters(in: .whitespacesAndNewlines), !message.isEmpty {
                            return message
                        }
                    }

                    if let raw = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines), !raw.isEmpty {
                        return raw
                    }
                }
                return String(format: NSLocalizedString("Ошибка сервера (%@).", comment: "Chat creation server status"), "\(statusCode)")
            }
        }

        return NSLocalizedString("Произошла неизвестная ошибка. Попробуйте позже.", comment: "Chat creation unknown error")
    }

    private func loadAvatarIfNeeded(for contact: Contact) {
        guard !contact.isDeleted else { return }
        let contactId = contact.id
        if avatarLoadedIds.contains(contactId) || avatarLoadingIds.contains(contactId) {
            return
        }

        avatarLoadingIds.insert(contactId)

        Task {
            do {
                let profile = try await profileService.fetchProfile(userId: contactId)
                await MainActor.run {
                    if let info = profile.avatars?.current {
                        contactAvatars[contactId] = info
                    }
                    avatarLoadedIds.insert(contactId)
                    avatarLoadingIds.remove(contactId)
                }
            } catch {
                if AppConfig.DEBUG {
                    print("[ContactsTab] load avatar failed for \(contactId): \(error)")
                }
                await MainActor.run {
                    avatarLoadingIds.remove(contactId)
                }
            }
        }
    }

    private func performBlockContact(_ contact: Contact) {
        let contactId = contact.id
        guard !blockingContactIds.contains(contactId) else { return }
        blockingContactIds.insert(contactId)

        Task {
            do {
                _ = try await blockedUsersService.add(userId: contactId)
                await MainActor.run {
                    blockingContactIds.remove(contactId)
                    handleContactRemoved(contactId)
                }
            } catch {
                await MainActor.run {
                    blockingContactIds.remove(contactId)
                    activeAlert = .error(message: error.localizedDescription)
                }
            }
        }
    }

    private func performDeleteContact(_ contact: Contact) {
        let contactId = contact.id
        guard !deletingContactIds.contains(contactId) else { return }
        deletingContactIds.insert(contactId)

        Task {
            do {
                try await contactsService.removeContact(userId: contactId)
                await MainActor.run {
                    deletingContactIds.remove(contactId)
                    handleContactRemoved(contactId)
                }
            } catch {
                await MainActor.run {
                    deletingContactIds.remove(contactId)
                    activeAlert = .error(message: error.localizedDescription)
                }
            }
        }
    }

    private func contactEditInfo(for contact: Contact) -> ContactEditInfo {
        ContactEditInfo(
            userId: contact.id,
            login: contact.login,
            fullName: contact.fullName,
            customName: contact.customName,
            avatarFileId: contactAvatars[contact.id]?.fileId
        )
    }

    private func handleContactRenamed(_ contactId: UUID, newName: String) {
        guard let index = contacts.firstIndex(where: { $0.id == contactId }) else { return }
        contacts[index] = contacts[index].updatingCustomName(newName)
    }

    private func handleContactRemoved(_ contactId: UUID) {
        contacts.removeAll { $0.id == contactId }
        contactAvatars.removeValue(forKey: contactId)
        avatarLoadedIds.remove(contactId)
        avatarLoadingIds.remove(contactId)
        if creatingChatForContactId == contactId {
            creatingChatForContactId = nil
        }
        blockingContactIds.remove(contactId)
        deletingContactIds.remove(contactId)
        if contactToEdit?.id == contactId {
            contactToEdit = nil
        }
        if contactPendingBlock?.id == contactId {
            contactPendingBlock = nil
            showBlockConfirmation = false
        }
        if contactPendingDelete?.id == contactId {
            contactPendingDelete = nil
            showDeleteConfirmation = false
        }
    }

    private func isRowBusy(_ contact: Contact) -> Bool {
        creatingChatForContactId == contact.id
            || blockingContactIds.contains(contact.id)
            || deletingContactIds.contains(contact.id)
    }
}

private struct ContactRow: View {
    let contact: Contact
    let avatarInfo: AvatarInfo?
    let currentUserId: String?
    let isLoading: Bool

    private let avatarSize: CGFloat = 40

    init(contact: Contact, avatarInfo: AvatarInfo? = nil, currentUserId: String? = nil, isLoading: Bool = false) {
        self.contact = contact
        self.avatarInfo = avatarInfo
        self.currentUserId = currentUserId
        self.isLoading = isLoading
    }

    var body: some View {
        HStack(alignment: .top, spacing: 12) {
            avatarView

            VStack(alignment: .leading, spacing: 3) {
                HStack(alignment: .firstTextBaseline) {
                    if #available(iOS 16.0, *) {
                        Text(contact.displayName)
                            .font(.subheadline.weight(.semibold))
                            .foregroundColor(.primary)
                            .strikethrough(contact.isDeleted, color: .secondary)
                    } else {
                        Text(contact.displayName)
                            .font(.subheadline.weight(.semibold))
                            .foregroundColor(.primary)
                            .strikethrough(contact.isDeleted)
                    }
                    Spacer()
                    Text(contact.formattedCreatedAt)
                        .font(.caption2)
                        .foregroundColor(.secondary)
                }

                if let handle = contact.handle {
                    Text(handle)
                        .font(.caption2)
                        .foregroundColor(.secondary)
                }

                if contact.friendCode {
                    friendCodeBadge
                }
            }
            .frame(maxWidth: .infinity, alignment: .leading)

            if isLoading {
                ProgressView()
                    .scaleEffect(0.8)
                    .padding(.top, 4)
            }
        }
        .padding(.vertical, 6)
    }

    @ViewBuilder
    private var avatarView: some View {
        if let fileId = avatarInfo?.fileId,
           let url = avatarURL(for: fileId),
           let currentUserId {
            CachedAvatarView(url: url, fileId: fileId, userId: currentUserId) {
                placeholderAvatar
            }
            .aspectRatio(contentMode: .fill)
            .frame(width: avatarSize, height: avatarSize)
            .clipShape(Circle())
        } else {
            placeholderAvatar
        }
    }

    private func avatarURL(for fileId: String) -> URL? {
        let userId = contact.id.uuidString
        let path = "\(AppConfig.API_SERVER)/v1/storage/download/avatar/\(userId)?file_id=\(fileId)"
        return URL(string: path)
    }

    private var placeholderAvatar: some View {
        Circle()
            .fill(avatarBackgroundColor)
            .frame(width: avatarSize, height: avatarSize)
            .overlay(
                Group {
                    if contact.isDeleted {
                        Image(systemName: "person.slash")
                            .symbolRenderingMode(.hierarchical)
                            .font(.system(size: avatarSize * 0.45, weight: .semibold))
                            .foregroundColor(avatarTextColor)
                    } else {
                        Text(contact.initials)
                            .font(.system(size: avatarSize * 0.5, weight: .semibold))
                            .foregroundColor(avatarTextColor)
                    }
                }
            )
    }

    private var avatarBackgroundColor: Color {
        contact.isDeleted ? Color(.systemGray5) : Color.accentColor.opacity(0.15)
    }

    private var avatarTextColor: Color {
        contact.isDeleted ? Color.accentColor : Color.accentColor
    }

    private var friendCodeBadge: some View {
        Text(NSLocalizedString("Код дружбы", comment: "Friend code badge"))
            .font(.caption2.weight(.medium))
            .foregroundColor(Color.accentColor)
            .padding(.horizontal, 6)
            .padding(.vertical, 3)
            .background(Color.accentColor.opacity(0.12))
            .clipShape(Capsule())
    }
}

private struct Contact: Identifiable, Equatable {
    let id: UUID
    let login: String?
    let fullName: String?
    let customName: String?
    let friendCode: Bool
    let createdAt: Date

    let displayName: String
    let handle: String?

    var isDeleted: Bool {
        login?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? true
    }

    var initials: String {
        if isDeleted { return "" }

        let nameSource: String?
        if let customName, !customName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
            nameSource = customName
        } else if let fullName, !fullName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
            nameSource = fullName
        } else {
            nameSource = nil
        }

        if let name = nameSource {
            let components = name.split(separator: " ")
            let nameInitials = components.prefix(2).compactMap { $0.first }
            if !nameInitials.isEmpty {
                return nameInitials.map { String($0) }.joined().uppercased()
            }
        }
        
        return String(login!.prefix(1)).uppercased()
    }

    var formattedCreatedAt: String {
        Self.relativeFormatter.localizedString(for: createdAt, relativeTo: Date())
    }

    init(payload: ContactPayload) {
        self.id = payload.userId
        self.login = payload.login
        self.fullName = payload.fullName
        self.customName = payload.customName
        self.friendCode = payload.friendCode
        self.createdAt = payload.createdAt

        let isUserDeleted = payload.login?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? true

        if isUserDeleted {
            self.displayName = NSLocalizedString("Неизвестный пользователь", comment: "Deleted user display name")
            self.handle = nil
        } else {
            if let customName = payload.customName, !customName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
                self.displayName = customName
            } else if let fullName = payload.fullName, !fullName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
                self.displayName = fullName
            } else {
                self.displayName = payload.login!
            }

            if let login = payload.login, !login.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
                self.handle = "@\(login)"
            } else {
                self.handle = nil
            }
        }
    }

    func updatingCustomName(_ newName: String) -> Contact {
        let trimmed = newName.trimmingCharacters(in: .whitespacesAndNewlines)
        let updatedCustomName = trimmed.isEmpty ? nil : trimmed
        let payload = ContactPayload(
            userId: id,
            login: login,
            fullName: fullName,
            customName: updatedCustomName,
            friendCode: friendCode,
            createdAt: createdAt
        )
        return Contact(payload: payload)
    }

    private static let relativeFormatter: RelativeDateTimeFormatter = {
        let formatter = RelativeDateTimeFormatter()
        formatter.unitsStyle = .short
        return formatter
    }()
}

private enum ContactsAlert: Identifiable {
    case error(id: UUID = UUID(), message: String)
    case info(id: UUID = UUID(), title: String, message: String)

    var id: String {
        switch self {
        case .error(let id, _), .info(let id, _, _):
            return id.uuidString
        }
    }
}

private enum ContactAction {
    case edit
    case block
    case delete
}
