import SwiftUI

struct BlockedUsersView: View {
    @State private var blockedUsers: [BlockedUser] = []
    @State private var isLoading = false
    @State private var hasMore = true
    @State private var offset = 0
    @State private var loadError: String?
    @State private var pendingUnblock: BlockedUser?
    @State private var showUnblockConfirmation = false
    @State private var removingUserIds: Set<UUID> = []
    @State private var activeAlert: ActiveAlert?
    @State private var errorMessageDown: String?
    @State private var isAddUserSheetPresented = false
    @State private var newBlockedUserLogin = ""
    @State private var addBlockedUserError: String?
    @State private var isProcessingAddBlockedUser = false
    @FocusState private var isAddBlockedUserFieldFocused: Bool

    private let blockedUsersService = BlockedUsersService()
    private let limit = 20

    var body: some View {
        List {
            if isLoading && blockedUsers.isEmpty {
                initialLoadingState
            } else if let loadError, blockedUsers.isEmpty {
                errorState(loadError)
            } else if blockedUsers.isEmpty {
                emptyState
            } else {
                usersSection
            }
        }
        .navigationTitle(NSLocalizedString("Чёрный список", comment: ""))
        .navigationBarTitleDisplayMode(.inline)
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                Button {
                    isAddUserSheetPresented = true
                } label: {
                    Image(systemName: "plus")
                }
            }
        }
        .task {
            await loadBlockedUsers()
        }
        .sheet(isPresented: $isAddUserSheetPresented, onDismiss: resetAddBlockedUserForm) {
            addBlockedUserSheet
        }
        .alert(item: $activeAlert) { alert in
            switch alert {
            case .error(_, let message):
                return Alert(
                    title: Text(NSLocalizedString("Ошибка", comment: "Common error title")),
                    message: Text(message),
                    dismissButton: .default(Text(NSLocalizedString("OK", comment: "Common OK")))
                )
            }
        }
        .confirmationDialog(
            NSLocalizedString("Удалить из заблокированных?", comment: "Unblock confirmation title"),
            isPresented: $showUnblockConfirmation,
            presenting: pendingUnblock
        ) { user in
            Button(NSLocalizedString("Разблокировать", comment: "Unblock confirmation action"), role: .destructive) {
                pendingUnblock = nil
                showUnblockConfirmation = false
                Task {
                    await unblock(user)
                }
            }
            Button(NSLocalizedString("Отмена", comment: "Common cancel"), role: .cancel) {
                pendingUnblock = nil
                showUnblockConfirmation = false
            }
        } message: { user in
            Text(String(format: NSLocalizedString("Пользователь \"%1$@\" будет удалён из списка заблокированных.", comment: "Unblock confirmation message"), user.displayName))
        }
    }

    private var addBlockedUserSheet: some View {
        NavigationView {
            Form {
                Section(
                    header: Text(NSLocalizedString("Логин пользователя", comment: "Blocked users add login header")),
                    footer: Text(NSLocalizedString("Введите юзернейм человека, которого нужно заблокировать. Символ @ указывать не нужно.", comment: "Blocked users add login footer"))
                        .font(.footnote)
                        .foregroundColor(.secondary)
                ) {
                    TextField(NSLocalizedString("Например, username", comment: "Blocked users add login placeholder"), text: $newBlockedUserLogin)
                        .textInputAutocapitalization(.never)
                        .autocorrectionDisabled()
                        .keyboardType(.asciiCapable)
                        .focused($isAddBlockedUserFieldFocused)
                }

                if let addBlockedUserError {
                    Section {
                        Text(addBlockedUserError)
                            .font(.footnote)
                            .foregroundColor(.red)
                            .multilineTextAlignment(.leading)
                    }
                }
            }
            .navigationTitle(NSLocalizedString("Заблокировать", comment: "Blocked users add title"))
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .cancellationAction) {
                    Button(NSLocalizedString("Отмена", comment: "Common cancel")) {
                        isAddUserSheetPresented = false
                    }
                }
                ToolbarItem(placement: .confirmationAction) {
                    if isProcessingAddBlockedUser {
                        ProgressView()
                    } else {
                        Button(NSLocalizedString("Заблокировать", comment: "Blocked users add confirm")) {
                            submitAddBlockedUser()
                        }
                        .disabled(!canSubmitNewBlockedUser)
                    }
                }
            }
            .onAppear {
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                    isAddBlockedUserFieldFocused = true
                }
            }
        }
    }

    private var usersSection: some View {
        Section(header: Text(NSLocalizedString("Заблокированные", comment: ""))) {
            ForEach(Array(blockedUsers.enumerated()), id: \.element.id) { index, user in
                userRow(user, index: index)
            }
            if isLoading && !blockedUsers.isEmpty {
                Text("Идет загрузка...")
                    .foregroundColor(.gray)
                    .frame(maxWidth: .infinity, alignment: .center)
                    .listRowBackground(Color.clear)
                    .listRowSeparator(.hidden)
            } else if let errorMessage = errorMessageDown {
                Text(errorMessage)
                    .foregroundColor(.red)
                    .frame(maxWidth: .infinity, alignment: .center)
                    .listRowBackground(Color.clear)
                    .listRowSeparator(.hidden)
            }
        }
    }

    private var trimmedNewBlockedUserLogin: String {
        newBlockedUserLogin.trimmingCharacters(in: .whitespacesAndNewlines)
    }

    private var canSubmitNewBlockedUser: Bool {
        !trimmedNewBlockedUserLogin.isEmpty && !isProcessingAddBlockedUser
    }

    private func userRow(_ user: BlockedUser, index: Int) -> some View {
        HStack(spacing: 12) {
            Circle()
                .fill(user.isDeleted ? Color(.systemGray5) : Color.accentColor.opacity(0.15))
                .frame(width: 44, height: 44)
                .overlay(
                    Group {
                        if user.isDeleted {
                            Image(systemName: "person.slash")
                                .font(.headline)
                                .foregroundColor(Color(.systemGray2))
                        } else {
                            Text(user.initials)
                                .font(.headline)
                                .foregroundColor(.accentColor)
                        }
                    }
                )
            VStack(alignment: .leading, spacing: 4) {
                if #available(iOS 16.0, *) {
                    Text(user.displayName)
                        .font(.body)
                        .strikethrough(user.isDeleted, color: .secondary)
                } else {
                    Text(user.displayName)
                        .font(.body)
                        .strikethrough(user.isDeleted)
                }
                if let handle = user.handle {
                    Text(handle)
                        .font(.caption)
                        .foregroundColor(.secondary)
                }
            }
            Spacer()
        }
        .padding(.vertical, 0)
        .swipeActions(edge: .trailing) {
            Button(role: .destructive) {
                pendingUnblock = user
                showUnblockConfirmation = true
            } label: {
                Label(NSLocalizedString("Разблокировать", comment: ""), systemImage: "person.crop.circle.badge.xmark")
            }
//            .disabled(removingUserIds.contains(user.id) || user.isDeleted)
            .disabled(removingUserIds.contains(user.id))
        }
        .onAppear {
            if index >= blockedUsers.count - 5 {
                Task {
                    await loadBlockedUsers()
                }
            }
        }
    }

    private var emptyState: some View {
        VStack(spacing: 12) {
            Image(systemName: "hand.raised")
                .font(.system(size: 48))
                .foregroundColor(.secondary)
            Text(NSLocalizedString("У вас нет заблокированных пользователей", comment: ""))
                .font(.headline)
                .multilineTextAlignment(.center)
        }
        .frame(maxWidth: .infinity, alignment: .center)
        .padding(.vertical, 32)
        .listRowInsets(EdgeInsets(top: 16, leading: 16, bottom: 16, trailing: 16))
        .listRowSeparator(.hidden)
    }

    private var initialLoadingState: some View {
        Section {
            ProgressView()
                .frame(maxWidth: .infinity, alignment: .center)
        }
    }

    private func errorState(_ message: String) -> some View {
        Section {
            Text(message)
                .foregroundColor(.red)
                .frame(maxWidth: .infinity, alignment: .center)
        }
    }

    private func submitAddBlockedUser() {
        guard canSubmitNewBlockedUser else { return }

        let login = trimmedNewBlockedUserLogin
        isProcessingAddBlockedUser = true
        addBlockedUserError = nil

        Task {
            await performAddBlockedUser(login: login)
        }
    }

    private func resetAddBlockedUserForm() {
        newBlockedUserLogin = ""
        addBlockedUserError = nil
        isProcessingAddBlockedUser = false
        isAddBlockedUserFieldFocused = false
    }

    private func performAddBlockedUser(login: String) async {
        do {
            let payload = try await blockedUsersService.add(login: login)
            let newUser = BlockedUser(payload: payload)

            await MainActor.run {
                let existed = blockedUsers.contains(where: { $0.id == newUser.id })
                blockedUsers.removeAll { $0.id == newUser.id }
                blockedUsers.insert(newUser, at: 0)
                if !existed {
                    offset += 1
                }
                isAddUserSheetPresented = false
            }
        } catch {
            if AppConfig.DEBUG {
                print("[BlockedUsersView] add blocked user failed: \(error)")
            }
            await MainActor.run {
                addBlockedUserError = error.localizedDescription
            }
        }

        await MainActor.run {
            isProcessingAddBlockedUser = false
        }
    }

    @MainActor
    private func loadBlockedUsers() async {
        errorMessageDown = nil
        guard !isLoading, hasMore else {
            return
        }

        isLoading = true
        defer { isLoading = false }

        if offset == 0 {
            loadError = nil
        }

        do {
            let payload = try await blockedUsersService.fetchBlockedUsers(limit: limit, offset: offset)
            blockedUsers.append(contentsOf: payload.items.map(BlockedUser.init))
            offset += payload.items.count
            hasMore = payload.hasMore
        } catch {
            let message = error.localizedDescription
            if offset == 0 {
                loadError = message
            }
//            activeAlert = .error(message: message)
            errorMessageDown = message
            if AppConfig.DEBUG { print("[BlockedUsersView] load blocked users failed: \(error)") }
        }
    }

    @MainActor
    private func unblock(_ user: BlockedUser) async {
        guard !removingUserIds.contains(user.id) else { return }

        removingUserIds.insert(user.id)
        defer { removingUserIds.remove(user.id) }

        do {
            _ = try await blockedUsersService.remove(userId: user.id)
            blockedUsers.removeAll { $0.id == user.id }
        } catch {
            activeAlert = .error(message: error.localizedDescription)
            if AppConfig.DEBUG { print("[BlockedUsersView] unblock failed: \(error)") }
        }
    }
}

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

    private(set) var displayName: String
    private(set) var 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()
    }

    init(payload: BlockedUserInfo) {
        self.id = payload.userId
        self.login = payload.login
        self.fullName = payload.fullName
        self.customName = payload.customName
        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!
            }

            self.handle = "@\(payload.login!)"
        }
    }
}

private enum ActiveAlert: Identifiable {
    case error(id: UUID = UUID(), message: String)

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