//
//  ChatsTab.swift
//  VolnahubApp
//
//  Created by cheykrym on 09/06/2025.
//

import SwiftUI
#if canImport(UIKit)
import UIKit
#endif

struct ChatsTab: View {
    @ObservedObject private var loginViewModel: LoginViewModel
    @Binding var searchRevealProgress: CGFloat
    @Binding var searchText: String
    private let searchService = SearchService()
    private let chatService = ChatService()
    @AppStorage("chatRowMessageLineLimit") private var messageLineLimitSetting: Int = 2
    @StateObject private var viewModel = PrivateChatsViewModel()
    @State private var searchDragStartProgress: CGFloat = 0
    @State private var isSearchGestureActive: Bool = false
    @State private var globalSearchResults: [UserSearchResult] = []
    @State private var isGlobalSearchLoading: Bool = false
    @State private var globalSearchError: String?
    @State private var globalSearchTask: Task<Void, Never>? = nil
    @State private var creatingChatForUserId: UUID?
    @State private var chatCreationError: String?
    @State private var isChatCreationAlertPresented: Bool = false
    @State private var pendingChatItem: PrivateChatListItem?
    @State private var isPendingChatActive: Bool = false

    private let searchRevealDistance: CGFloat = 90
    private let scrollToTopAnchorId = "ChatsListTopAnchor"

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

    init(
        loginViewModel: LoginViewModel,
        searchRevealProgress: Binding<CGFloat>,
        searchText: Binding<String>
    ) {
        self._loginViewModel = ObservedObject(wrappedValue: loginViewModel)
        self._searchRevealProgress = searchRevealProgress
        self._searchText = searchText
    }

    var body: some View {
        content
            .navigationTitle(NSLocalizedString("Чаты", comment: "Chats tab title"))
            .background(Color(UIColor.systemBackground))
            .onAppear {
                viewModel.loadInitialChats()
                handleSearchQueryChange(searchText)
            }
            .onReceive(NotificationCenter.default.publisher(for: .debugRefreshChats)) { _ in
                if loginViewModel.chatLoadingState != .loading {
                    loginViewModel.chatLoadingState = .loading
                }
                viewModel.refresh()
            }
            .onReceive(NotificationCenter.default.publisher(for: .chatsShouldRefresh)) { _ in
                if loginViewModel.chatLoadingState != .loading {
                    loginViewModel.chatLoadingState = .loading
                }
                viewModel.refresh()
            }
            .onChange(of: searchText) { newValue in
                handleSearchQueryChange(newValue)
                let trimmed = newValue.trimmingCharacters(in: .whitespacesAndNewlines)
                if !trimmed.isEmpty && searchRevealProgress < 1 {
                    withAnimation(.spring(response: 0.35, dampingFraction: 0.75)) {
                        searchRevealProgress = 1
                    }
                }
            }
            .alert(
                NSLocalizedString("Не удалось открыть чат", comment: "Chat creation error title"),
                isPresented: Binding(
                    get: { isChatCreationAlertPresented && chatCreationError != nil },
                    set: { newValue in
                        isChatCreationAlertPresented = newValue
                        if !newValue {
                            chatCreationError = nil
                        }
                    }
                ),
                presenting: chatCreationError
            ) { _ in
                Button(NSLocalizedString("Понятно", comment: "Chat creation error acknowledgment"), role: .cancel) { }
            } message: { error in
                Text(error)
            }
            .onDisappear {
                globalSearchTask?.cancel()
                globalSearchTask = nil
            }
    }

    @ViewBuilder
    private var content: some View {
//        if viewModel.isInitialLoading && viewModel.chats.isEmpty {
//            loadingState
//        }
        chatList
        
    }

    private var chatList: some View {
        ScrollViewReader { proxy in
            ZStack {
                List {
//            VStack(spacing: 0) {
//                searchBar
//                    .padding(.horizontal, 16)
//                    .padding(.vertical, 8)
//            }
//            .background(Color(UIColor.systemBackground))

                    if let message = viewModel.errorMessage {
                        Section {
                            HStack(alignment: .top, spacing: 8) {
                                Image(systemName: "exclamationmark.triangle.fill")
                                    .foregroundColor(.orange)
                                Text(message)
                                    .font(.subheadline)
                                    .foregroundColor(.orange)
                                Spacer(minLength: 0)
                                Button(action: triggerChatsReload) {
                                    Text(NSLocalizedString("Обновить", comment: ""))
                                        .font(.subheadline)
                                }
                                .buttonStyle(.borderless)
                            }
                            .padding(.vertical, 4)
                        }
                        .listRowInsets(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16))
                    }

                    if isSearching {
                        Section(header: localSearchHeader) {
                            if localSearchResults.isEmpty {
                                emptySearchResultView
                                    .listRowInsets(EdgeInsets(top: 24, leading: 16, bottom: 24, trailing: 16))
                                    .listRowSeparator(.hidden)
                            } else {
                                let firstLocalChatId = localSearchResults.first?.chatId
                                ForEach(localSearchResults) { chat in
                                    chatRowItem(for: chat)
                                        .id(chat.chatId == firstLocalChatId ? scrollToTopAnchorId : chat.chatId)
                                }
                            }
                        }

                        Section(header: globalSearchHeader) {
                            globalSearchContent
                        }
                    } else {
//                    if let message = viewModel.errorMessage, viewModel.chats.isEmpty {
//                        errorState(message: message)
//                    } else
                        if viewModel.isInitialLoading && viewModel.chats.isEmpty {
                            loadingState
                        }

                        if viewModel.chats.isEmpty {
                            emptyState
                        } else {

                            let firstChatId = viewModel.chats.first?.chatId
                            ForEach(viewModel.chats) { chat in
                                chatRowItem(for: chat)
                                    .id(chat.chatId == firstChatId ? scrollToTopAnchorId : chat.chatId)
                            }

                            if viewModel.isLoadingMore {
                                loadingMoreRow
                            }
                        }
                    }
                }
                .listStyle(.plain)
                .modifier(ScrollDismissesKeyboardModifier())
                .simultaneousGesture(searchBarGesture)
                .simultaneousGesture(tapToDismissKeyboardGesture)
                .onReceive(NotificationCenter.default.publisher(for: .chatsShouldScrollToTop)) { _ in
                    scrollChatsToTop(using: proxy)
                }
//            .safeAreaInset(edge: .top) {
//                VStack(spacing: 0) {
//                    searchBar
//                        .padding(.horizontal, 16)
//                        .padding(.top, 8)
//                        .padding(.bottom, 8)
//                    Divider()
//                }
//                .background(Color(UIColor.systemBackground))
//            }

                pendingChatNavigationLink
            }
        }
    }

    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 scrollChatsToTop(using proxy: ScrollViewProxy) {
        DispatchQueue.main.async {
            withAnimation(.spring(response: 0.35, dampingFraction: 0.75)) {
                proxy.scrollTo(scrollToTopAnchorId, anchor: .top)
            }
        }
    }

    private var searchBarGesture: some Gesture {
        DragGesture(minimumDistance: 10, coordinateSpace: .local)
            .onChanged { value in
                let verticalTranslation = value.translation.height
                let horizontalTranslation = value.translation.width

                if !isSearchGestureActive {
                    guard abs(verticalTranslation) > abs(horizontalTranslation) else { return }
                    if searchRevealProgress <= 0.001 && verticalTranslation < 0 { return }
                    isSearchGestureActive = true
                    searchDragStartProgress = searchRevealProgress
                }

                guard isSearchGestureActive else { return }

                let delta = verticalTranslation / searchRevealDistance
                let newProgress = searchDragStartProgress + delta
                if isSearching && newProgress < 1 {
                    searchRevealProgress = 1
                    return
                }
                searchRevealProgress = max(0, min(1, newProgress))
            }
            .onEnded { _ in
                guard isSearchGestureActive else { return }
                isSearchGestureActive = false

                if isSearching {
                    withAnimation(.spring(response: 0.35, dampingFraction: 0.75)) {
                        searchRevealProgress = 1
                    }
                    return
                }

                let target: CGFloat = searchRevealProgress > 0.5 ? 1 : 0
                withAnimation(.spring(response: 0.35, dampingFraction: 0.75)) {
                    searchRevealProgress = target
                }
            }
    }

    private var tapToDismissKeyboardGesture: some Gesture {
        TapGesture().onEnded {
            dismissKeyboard()
        }
    }

    private var isSearching: Bool {
        !searchText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
    }

    private var messagePreviewLineLimit: Int {
        switch messageLineLimitSetting {
        case ..<1:
            return 1
        case 1...2:
            return messageLineLimitSetting
        default:
            return 2
        }
    }

    private var filteredChats: [PrivateChatListItem] {
        let trimmedQuery = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !trimmedQuery.isEmpty else { return viewModel.chats }
        let lowercasedQuery = trimmedQuery.lowercased()
        return viewModel.chats.filter { chat in
            let searchableValues = [
                chat.chatData?.customName,
                chat.chatData?.fullName,
                chat.chatData?.login,
                chat.lastMessage?.content
            ]
            return searchableValues
                .compactMap { $0?.lowercased() }
                .contains(where: { $0.contains(lowercasedQuery) })
        }
    }

    private var localSearchResults: [PrivateChatListItem] {
        Array(filteredChats.prefix(5))
    }

    private var localSearchHeader: some View {
        Text(NSLocalizedString("Локальные чаты", comment: "Local search section"))
    }

    private var globalSearchHeader: some View {
        Text(NSLocalizedString("Глобальный поиск", comment: "Global search section"))
    }

    @ViewBuilder
    private var globalSearchContent: some View {
        if isGlobalSearchLoading {
            globalSearchLoadingRow
        } else
//        if let error = globalSearchError {
//            globalSearchErrorRow(message: error)
//        } else
        if globalSearchResults.isEmpty {
            globalSearchEmptyRow
        } else {
            let firstGlobalUserId = globalSearchResults.first?.id
            ForEach(globalSearchResults) { user in
                globalSearchRow(for: user)
                    .id(user.id == firstGlobalUserId ? AnyHashable(scrollToTopAnchorId) : AnyHashable(user.id))
            }
        }
    }

    private var emptySearchResultView: some View {
        VStack(spacing: 8) {
            Image(systemName: "text.magnifyingglass")
                .font(.system(size: 40))
                .foregroundColor(.secondary)
            Text(NSLocalizedString("Ничего не найдено", comment: ""))
                .font(.headline)
            Text(NSLocalizedString("Попробуйте изменить запрос поиска.", comment: ""))
                .font(.subheadline)
                .foregroundColor(.secondary)
                .multilineTextAlignment(.center)
        }
        .frame(maxWidth: .infinity)
    }

//    private var loadingState: some View {
//        VStack(spacing: 12) {
//            ProgressView()
//            Text(NSLocalizedString("Загружаем чаты…", comment: ""))
//                .font(.subheadline)
//                .foregroundColor(.secondary)
//        }
//        .frame(maxWidth: .infinity, maxHeight: .infinity)
//    }
    
    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 {
        VStack(spacing: 12) {
            Image(systemName: "exclamationmark.bubble")
                .font(.system(size: 48))
                .foregroundColor(.orange)
            Text(message)
                .font(.body)
                .multilineTextAlignment(.center)
                .foregroundColor(.primary)
            Button(action: triggerChatsReload) {
                Text(NSLocalizedString("Повторить", comment: ""))
                    .font(.headline)
            }
            .buttonStyle(.borderedProminent)
        }
        .padding()
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }

    private var emptyState: some View {
        VStack(spacing: 12) {
//            Image(systemName: "bubble.left")
//                .font(.system(size: 48))
//                .foregroundColor(.secondary)
            Text(NSLocalizedString("Пока что у вас нет чатов", comment: ""))
                .font(.body)
                .foregroundColor(.secondary)
//            Button(action: triggerChatsReload) {
//                Text(NSLocalizedString("Обновить", comment: ""))
//            }
            .buttonStyle(.bordered)
        }
        .padding()
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .listRowInsets(EdgeInsets(top: 24, leading: 16, bottom: 24, trailing: 16))
        .listRowSeparator(.hidden)
        .listRowBackground(Color.clear)
    }

    private var loadingMoreRow: some View {
        HStack {
            Spacer()
            ProgressView()
                .padding(.vertical, 12)
            Spacer()
        }
        .listRowSeparator(.hidden)
    }

    private func triggerChatsReload() {
        if loginViewModel.chatLoadingState != .loading {
            loginViewModel.chatLoadingState = .loading
        }
        viewModel.loadInitialChats(force: true)
    }

    @ViewBuilder
    private func chatRowItem(for chat: PrivateChatListItem) -> some View {
        Button {
            openChat(chat)
        } label: {
            ChatRowView(
                chat: chat,
                currentUserId: currentUserId,
                messageLimitLine: messagePreviewLineLimit
            )
                .contentShape(Rectangle())
        }
        .buttonStyle(.plain)
        .contextMenu {
            Button(action: {}) {
                Label(NSLocalizedString("Закрепить (скоро)", comment: ""), systemImage: "pin")
            }

            Button(action: {}) {
                Label(NSLocalizedString("Без звука (скоро)", comment: ""), systemImage: "speaker.slash")
            }

            Button(role: .destructive, action: {}) {
                Label(NSLocalizedString("Удалить чат (скоро)", comment: ""), systemImage: "trash")
            }
        }
        .listRowInsets(EdgeInsets(top: 4, leading: 16, bottom: 4, trailing: 16))
//        .listRowSeparator(.hidden)
        .onAppear {
            guard !isSearching else { return }
            viewModel.loadMoreIfNeeded(currentItem: chat)
        }
    }


    private func openChat(_ chat: PrivateChatListItem) {
        pendingChatItem = chat
        isPendingChatActive = true
    }

    private var globalSearchLoadingRow: some View {
        HStack {
            ProgressView()
                .progressViewStyle(CircularProgressViewStyle())
            Text(NSLocalizedString("Ищем пользователей…", comment: "Global search loading"))
                .font(.footnote)
        }
        .frame(maxWidth: .infinity, alignment: .leading)
        .padding(.vertical, 12)
        .listRowInsets(EdgeInsets(top: 12, leading: 16, bottom: 12, trailing: 16))
        .listRowSeparator(.hidden)
    }

    private func globalSearchErrorRow(message: String) -> some View {
        Text(message)
            .font(.footnote)
            .foregroundColor(.orange)
            .padding(.vertical, 12)
            .frame(maxWidth: .infinity, alignment: .leading)
            .listRowInsets(EdgeInsets(top: 12, leading: 16, bottom: 12, trailing: 16))
            .listRowSeparator(.hidden)
    }

    private var globalSearchEmptyRow: some View {
        Text(NSLocalizedString("Ничего не найдено", comment: "Global search empty state"))
            .font(.footnote)
            .foregroundColor(.secondary)
            .padding(.vertical, 12)
            .frame(maxWidth: .infinity, alignment: .leading)
            .listRowInsets(EdgeInsets(top: 12, leading: 16, bottom: 12, trailing: 16))
            .listRowSeparator(.hidden)
    }

    private func globalSearchRow(for user: UserSearchResult) -> some View {
        Button {
            openPrivateChat(for: user)
        } label: {
            HStack(spacing: 12) {
                Circle()
                    .fill(user.isOfficial ? Color.accentColor.opacity(0.85) : Color.accentColor.opacity(0.15))
                    .frame(width: 44, height: 44)
                    .overlay(
                        Text(user.avatarInitial)
                            .font(.headline)
                            .foregroundColor(user.isOfficial ? .white : Color.accentColor)
                    )

                VStack(alignment: .leading, spacing: 4) {
                    HStack(spacing: 6) {
                        Text(user.displayName)
                            .fontWeight(user.isOfficial ? .semibold : .regular)
                            .foregroundColor(.primary)
                            .lineLimit(1)
                            .truncationMode(.tail)

                        if user.isOfficial {
                            Image(systemName: "checkmark.seal.fill")
                                .foregroundColor(Color.accentColor)
                                .font(.caption)
                        }
                    }

                    if let secondary = secondaryLine(for: user) {
                        Text(secondary)
                            .font(.footnote)
                            .foregroundColor(.secondary)
                            .lineLimit(1)
                            .truncationMode(.tail)
                    }
                }
                .frame(maxWidth: .infinity, alignment: .leading)

                if creatingChatForUserId == user.userId {
                    ProgressView()
                        .progressViewStyle(CircularProgressViewStyle())
                }
            }
            .padding(.vertical, 8)
        }
        .buttonStyle(.plain)
        .disabled(creatingChatForUserId != nil)
        .listRowInsets(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16))
    }
}

private extension ChatsTab {
    func dismissKeyboard() {
#if canImport(UIKit)
        UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
#endif
    }

    func handleSearchQueryChange(_ query: String) {
        let trimmed = query.trimmingCharacters(in: .whitespacesAndNewlines)

        if trimmed.isEmpty {
            resetGlobalSearch()
            return
        }

        globalSearchTask?.cancel()
        globalSearchTask = nil

        guard trimmed.count >= 2 else {
            globalSearchResults = []
            globalSearchError = nil
            isGlobalSearchLoading = false
            return
        }

        isGlobalSearchLoading = true
        globalSearchError = nil

        globalSearchTask = Task {
            try? await Task.sleep(nanoseconds: 300_000_000)
            guard !Task.isCancelled else { return }

            do {
                let data = try await searchService.search(query: trimmed)
                guard !Task.isCancelled else { return }
                await MainActor.run {
                    globalSearchResults = data.users
                    isGlobalSearchLoading = false
                    globalSearchError = nil
                    globalSearchTask = nil
                }
            } catch is CancellationError {
                // Ignore cancellation
            } catch {
                guard !Task.isCancelled else { return }
                await MainActor.run {
                    globalSearchResults = []
                    isGlobalSearchLoading = false
                    globalSearchError = friendlyErrorMessage(for: error)
                    globalSearchTask = nil
                }
            }
        }
    }

    func resetGlobalSearch() {
        globalSearchTask?.cancel()
        globalSearchTask = nil
        globalSearchResults = []
        globalSearchError = nil
        isGlobalSearchLoading = false
    }

    func secondaryLine(for user: UserSearchResult) -> String? {
        if let official = user.officialFullName {
            if let custom = user.preferredCustomName, custom != official {
                return "\(user.loginHandle) (\(custom))"
            }
//            if official != user.loginHandle {
//                return user.loginHandle
//            }
        }
        else if let custom = user.preferredCustomName, custom != user.displayName {
            return custom
        }

        if let profileLogin = user.profile?.login.trimmingCharacters(in: .whitespacesAndNewlines), !profileLogin.isEmpty {
            let handle = "@\(profileLogin)"
            if handle != user.loginHandle {
                return handle
            }
        }

        if user.displayName != user.loginHandle {
            return user.loginHandle
        }

        return nil
    }

    func openPrivateChat(for user: UserSearchResult) {
        guard creatingChatForUserId == nil else { return }

        dismissKeyboard()
        creatingChatForUserId = user.userId
        chatCreationError = nil

        chatService.createOrFindPrivateChat(targetUserId: user.userId.uuidString) { result in
            creatingChatForUserId = nil

            switch result {
            case .success(let data):
                let chatItem = PrivateChatListItem(
                    chatId: data.chatId,
                    chatType: data.chatType,
                    chatData: chatProfile(from: user),
                    lastMessage: nil,
                    createdAt: nil,
                    unreadCount: 0
                )

                pendingChatItem = chatItem
                isPendingChatActive = true
                viewModel.refresh()
            case .failure(let error):
                chatCreationError = friendlyChatCreationMessage(for: error)
                isChatCreationAlertPresented = true
            }
        }
    }

    func chatProfile(from user: UserSearchResult) -> ChatProfile {
        let profile = user.profile
        let login = user.login ?? profile?.login
        let fullName = profile?.fullName
        let customName = user.preferredCustomName ?? profile?.customName

        return ChatProfile(
            userId: user.userId.uuidString,
            login: login,
            fullName: fullName,
            customName: customName,
            bio: profile?.bio,
            lastSeen: profile?.lastSeen,
            createdAt: user.createdAt ?? profile?.createdAt,
            isOfficial: user.isOfficial
        )
    }

    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")
    }


    func friendlyErrorMessage(for error: Error) -> String {
        if let searchError = error as? SearchServiceError {
            return searchError.errorDescription ?? NSLocalizedString("Не удалось выполнить поиск.", comment: "Search error fallback")
        }

        if let networkError = error as? NetworkError {
            switch networkError {
            case .unauthorized:
                return NSLocalizedString("Сессия истекла. Войдите снова.", comment: "Search error unauthorized")
            case .invalidURL, .noResponse:
                return NSLocalizedString("Ошибка соединения с сервером.", comment: "Search error connection")
            case .network(let underlying):
                return underlying.localizedDescription
            case .server(let statusCode, _):
                return String(format: NSLocalizedString("Сервер вернул ошибку (%d).", comment: "Search error server status"), statusCode)
            }
        }

        if (error as NSError).code == NSURLErrorCancelled { // доп. подстраховка
            return NSLocalizedString("Поиск отменён.", comment: "Search cancelled")
        }

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

private struct ScrollDismissesKeyboardModifier: ViewModifier {
    func body(content: Content) -> some View {
        if #available(iOS 16.0, *) {
            content.scrollDismissesKeyboard(.interactively)
        } else {
            content
        }
    }
}

private struct SearchResultPlaceholderView: View {
    let userId: UUID

    var body: some View {
        VStack(spacing: 16) {
            Text(NSLocalizedString("Профиль в разработке", comment: "Search placeholder title"))
                .font(.headline)

            VStack(alignment: .leading, spacing: 8) {
                Text(NSLocalizedString("Companion ID", comment: "Search placeholder companion title"))
                    .font(.subheadline)
                    .foregroundColor(.secondary)
                Text(userId.uuidString)
                    .font(.body.monospaced())
                    .contextMenu {
                        Button(action: {
#if canImport(UIKit)
                            UIPasteboard.general.string = userId.uuidString
#endif
                        }) {
                            Label(NSLocalizedString("Скопировать", comment: "Search placeholder copy"), systemImage: "doc.on.doc")
                        }
                    }
            }
            .frame(maxWidth: .infinity, alignment: .leading)
            .padding()
            .background(
                RoundedRectangle(cornerRadius: 12)
                    .fill(Color(UIColor.secondarySystemBackground))
            )

            Text(NSLocalizedString("Здесь появится информация о собеседнике и существующих чатах.", comment: "Search placeholder description"))
                .font(.footnote)
                .foregroundColor(.secondary)
                .multilineTextAlignment(.center)
                .padding(.horizontal)

            Spacer()
        }
        .padding(.top, 40)
        .padding(.horizontal, 24)
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
        .background(Color(UIColor.systemBackground))
    }
}

private struct ChatRowView: View {
    let chat: PrivateChatListItem
    let currentUserId: String?
    let messageLimitLine: Int
    private let avatarSize: CGFloat = 52

    private var title: String {
        switch chat.chatType {
        case .self:
            return NSLocalizedString("Избранные сообщения", comment: "")
        case .privateChat, .unknown:
            if let custom = chat.chatData?.customName, !custom.isEmpty {
                return custom
            }
            if let full = chat.chatData?.fullName, !full.isEmpty {
                return full
            }
            if let login = chat.chatData?.login, !login.isEmpty {
                return "@\(login)"
            }
            return NSLocalizedString("Неизвестный пользователь", comment: "")
        }
    }

    private var loginDisplay: String? {
        guard let login = chat.chatData?.login?.trimmingCharacters(in: .whitespacesAndNewlines), !login.isEmpty else {
            return nil
        }
        return "@\(login)"
    }

    private var isOfficial: Bool {
        chat.chatData?.isOfficial ?? false
    }

    private var officialDisplayName: String? {
        guard isOfficial else { return nil }

        if let customName = chat.chatData?.customName?.trimmingCharacters(in: .whitespacesAndNewlines), !customName.isEmpty {
            return customName
        }

        if let name = chat.chatData?.fullName?.trimmingCharacters(in: .whitespacesAndNewlines), !name.isEmpty {
            return NSLocalizedString(name, comment: "")
        }

        return loginDisplay
    }

    private var isDeletedUser: Bool {
        guard chat.chatType != .self else { return false }
        let login = chat.chatData?.login?.trimmingCharacters(in: .whitespacesAndNewlines)
        return login?.isEmpty ?? true
    }

    private var avatarBackgroundColor: Color {
        if isDeletedUser {
            return Color(.systemGray5)
        }
        return isOfficial ? Color.accentColor.opacity(0.85) : Color.accentColor.opacity(0.15)
    }

    private var avatarTextColor: Color {
        if isDeletedUser {
            return Color.accentColor
        }
        return isOfficial ? Color.white : Color.accentColor
    }

    private var messagePreview: String {
        guard let message = chat.lastMessage else {
            return NSLocalizedString("Нет сообщений", comment: "")
        }

        let body = messageBody(for: message)
        guard let prefix = authorPrefix(for: message) else {
            return body
        }
        return body.isEmpty ? prefix : "\(body)"
//        return body.isEmpty ? prefix : "\(prefix): \(body)"
    }

    private func messageBody(for message: MessageItem) -> String {
        if let content = trimmed(message.content), !content.isEmpty {
            return content
        }

        if message.mediaLink != nil {
            return NSLocalizedString("Вложение", comment: "")
        }

        return NSLocalizedString("Сообщение", comment: "")
    }

    private func authorPrefix(for message: MessageItem) -> String? {
        let isCurrentUser = currentUserId.map { $0 == message.senderId } ?? false
        if isCurrentUser {
            return NSLocalizedString("Вы", comment: "")
        }

        let profile = message.senderData ?? chat.chatData
        return displayName(for: profile)
    }

    private func displayName(for profile: ChatProfile?) -> String? {
        guard let profile else { return nil }

        let login = trimmed(profile.login)
        let customName = trimmed(profile.customName)
        let fullName = trimmed(profile.fullName)
        let isOfficialProfile = profile.isOfficial

        if let customName {
            return customName
        }

        if isOfficialProfile {
            if let fullName {
                return fullName
            }
            if let login {
                return "@\(login)"
            }
        }

        if let login {
            return "@\(login)"
        }

        return fullName
    }

    private func trimmed(_ string: String?) -> String? {
        guard let string = string?.trimmingCharacters(in: .whitespacesAndNewlines), !string.isEmpty else {
            return nil
        }
        return string
    }

    private var timestamp: String? {
        let date = chat.lastMessage?.createdAt ?? chat.createdAt
        guard let date else { return nil }
        return ChatRowView.formattedTimestamp(for: date)
    }

    private var initial: String {
        let nameSource: String?
        if let customName = chat.chatData?.customName, !customName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
            nameSource = customName
        } else if let fullName = chat.chatData?.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()
            }
        }
        
        if let login = chat.chatData?.login, !login.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
            return String(login.prefix(1)).uppercased()
        }
        
        return "?"
    }

    private var deletedUserSymbolName: String {
        return "person.slash"
    }

    private var subtitleColor: Color {
        chat.unreadCount > 0 ? .primary : .secondary
    }

    private var shouldShowReadStatus: Bool {
        guard let message = chat.lastMessage else { return false }
        let isCurrentUser = currentUserId.map { $0 == message.senderId } ?? false
        return isCurrentUser
    }

    private var readStatusIconName: String {
        guard let message = chat.lastMessage else { return "" }
        return message.isViewed == true ? "checkmark.circle.fill" : "checkmark.circle"
    }

    private var readStatusColor: Color {
        guard let message = chat.lastMessage else { return .secondary }
        return message.isViewed == true ? Color.accentColor : Color.secondary
    }
    
    private var avatarUrl: URL? {
        guard let chatData = chat.chatData,
              let fileId = chatData.avatars?.current?.fileId else {
            return nil
        }
        let userId = chatData.userId
        return URL(string: "\(AppConfig.API_SERVER)/v1/storage/download/avatar/\(userId)?file_id=\(fileId)")
    }

    var body: some View {
        HStack(spacing: 12) {
            if let url = avatarUrl, let fileId = chat.chatData?.avatars?.current?.fileId, let loggedInUserId = currentUserId {
                CachedAvatarView(url: url, fileId: fileId, userId: loggedInUserId) {
                    placeholderAvatar
                }
                .aspectRatio(contentMode: .fill)
                .frame(width: avatarSize, height: avatarSize)
                .clipShape(Circle())
            } else {
                placeholderAvatar
            }

            VStack(alignment: .leading, spacing: 4) {
                HStack{
                    if let officialName = officialDisplayName {
                        HStack(spacing: 6) {
                            if #available(iOS 16.0, *) {
                                Text(officialName)
                                    .fontWeight(.semibold)
                                    .foregroundColor(.primary)
                                    .lineLimit(1)
                                    .truncationMode(.tail)
                                    .strikethrough(isDeletedUser, color: Color.secondary)
                            } else {
                                Text(officialName)
                                    .fontWeight(.semibold)
                                    .foregroundColor(.primary)
                                    .lineLimit(1)
                                    .truncationMode(.tail)
                            }
                            
                            Image(systemName: "checkmark.seal.fill")
                                .foregroundColor(Color.accentColor)
                                .font(.caption)
                        }
                    } else {
                        if #available(iOS 16.0, *) {
                            Text(title)
                                .fontWeight(chat.unreadCount > 0 ? .semibold : .regular)
                                .foregroundColor(.primary)
                                .lineLimit(1)
                                .truncationMode(.tail)
                                .strikethrough(isDeletedUser, color: Color.secondary)
                        } else {
                            Text(title)
                                .fontWeight(chat.unreadCount > 0 ? .semibold : .regular)
                                .foregroundColor(.primary)
                                .lineLimit(1)
                                .truncationMode(.tail)
                        }
                    }

                    if let timestamp {
                        Spacer()
                        HStack(spacing: 4) {
                            if shouldShowReadStatus {
                                Image(systemName: readStatusIconName)
                                    .foregroundColor(readStatusColor)
                                    .font(.caption2)
                            }
    
                            Text(timestamp)
                                .font(.caption)
                                .foregroundColor(.secondary)
                        }
                    }
                    
                }
                
                HStack {
                    Text(messagePreview)
                        .font(.subheadline)
                        .foregroundColor(subtitleColor)
                        .lineLimit(messageLimitLine)
                        .truncationMode(.tail)

                    if chat.unreadCount > 0 {
                        Spacer()
                        Text("\(chat.unreadCount)")
                            .font(.caption2.bold())
                            .foregroundColor(.white)
                            .padding(.horizontal, 8)
                            .padding(.vertical, 4)
                            .background(
                                Capsule().fill(Color.accentColor)
                            )
                    }
                }
            }
            .frame(maxWidth: .infinity, alignment: .leading)

//            Spacer()

//            VStack(alignment: .trailing, spacing: 6) {
////                if let timestamp {
////                    HStack(spacing: 4) {
////                        if shouldShowReadStatus {
////                            Image(systemName: readStatusIconName)
////                                .foregroundColor(readStatusColor)
////                                .font(.caption2)
////                        }
////
////                        Text(timestamp)
////                            .font(.caption)
////                            .foregroundColor(.secondary)
////                    }
////                }
//
//                if chat.unreadCount > 0 {
//                    Text("\(chat.unreadCount)")
//                        .font(.caption2.bold())
//                        .foregroundColor(.white)
//                        .padding(.horizontal, 8)
//                        .padding(.vertical, 4)
//                        .background(
//                            Capsule().fill(Color.accentColor)
//                        )
//                }
//            }
        }
        .padding(.vertical, 8)
    }
    
    @ViewBuilder
    private var placeholderAvatar: some View {
        Circle()
            .fill(avatarBackgroundColor)
            .frame(width: avatarSize, height: avatarSize)
            .overlay(
                Group {
                    if isDeletedUser {
                        Image(systemName: deletedUserSymbolName)
                            .symbolRenderingMode(.hierarchical)
                            .font(.system(size: avatarSize * 0.45, weight: .semibold))
                            .foregroundColor(avatarTextColor)
                    } else {
                        Text(initial)
                            .font(.system(size: avatarSize * 0.5, weight: .semibold))
                            .foregroundColor(avatarTextColor)
                    }
                }
            )
    }

    private static func formattedTimestamp(for date: Date) -> String {
        let calendar = Calendar.current
        let locale = Locale.current

        let now = Date()
        let startOfNow = calendar.startOfDay(for: now)
        let startOfDate = calendar.startOfDay(for: date)
        let diff = calendar.dateComponents([.day], from: startOfDate, to: startOfNow).day ?? 0

        if diff <= 0 {
            return timeString(for: date, locale: locale)
        }

        if diff == 1 {
            return locale.identifier.lowercased().hasPrefix("ru") ? "Вчера" : "Yesterday"
        }

        if diff < 7 {
            return weekdayFormatter(for: locale).string(from: date)
        }

        if diff < 365 {
            return monthDayFormatter(for: locale).string(from: date)
        }

        return fullDateFormatter(for: locale).string(from: date)
    }

    private static func timeString(for date: Date, locale: Locale) -> String {
        let timeFormat = DateFormatter.dateFormat(fromTemplate: "j", options: 0, locale: locale) ?? ""
        let is12Hour = timeFormat.contains("a") || timeFormat.contains("h") || timeFormat.contains("K")

        let formatter = DateFormatter()
        formatter.locale = locale
        if is12Hour {
            formatter.dateFormat = DateFormatter.dateFormat(fromTemplate: "h:mm a", options: 0, locale: locale) ?? "h:mm a"
        } else {
            formatter.dateFormat = "HH:mm"
        }
        return formatter.string(from: date)
    }

    private static func weekdayFormatter(for locale: Locale) -> DateFormatter {
        let formatter = DateFormatter()
        formatter.locale = locale
        formatter.setLocalizedDateFormatFromTemplate("EEE")
        return formatter
    }

    private static func monthDayFormatter(for locale: Locale) -> DateFormatter {
        let formatter = DateFormatter()
        formatter.locale = locale
        formatter.setLocalizedDateFormatFromTemplate("MMM d")
        return formatter
    }

    private static func fullDateFormatter(for locale: Locale) -> DateFormatter {
        let formatter = DateFormatter()
        formatter.locale = locale
        formatter.dateFormat = "dd.MM.yy"
        return formatter
    }
}

struct ChatsTab_Previews: PreviewProvider {
    struct Wrapper: View {
        @State private var progress: CGFloat = 1
        @State private var searchText: String = ""
        @StateObject private var loginViewModel = LoginViewModel()

        var body: some View {
            ChatsTab(
                loginViewModel: loginViewModel,
                searchRevealProgress: $progress,
                searchText: $searchText
            )
            .environmentObject(ThemeManager())
        }
    }

    static var previews: some View {
        Wrapper()
    }
}

extension Notification.Name {
    static let debugRefreshChats = Notification.Name("debugRefreshChats")
    static let chatsShouldRefresh = Notification.Name("chatsShouldRefresh")
    static let chatsReloadCompleted = Notification.Name("chatsReloadCompleted")
    static let chatsShouldScrollToTop = Notification.Name("chatsShouldScrollToTop")
}
