import SwiftUI

struct EditProfileView: View {
    // State for form fields
    @State private var displayName = ""
    @State private var description = ""
    @State private var originalDisplayName = ""
    @State private var originalDescription = ""
    
    // State for profile data and avatar
    @State private var profile: ProfileDataPayload?
    @State private var avatarImage: UIImage?
    @State private var showImagePicker = false
    
    // State for loading and errors
    @State private var isLoading = false
    @State private var isSaving = false
    @State private var isUploadingAvatar = false
    @State private var isPreparingDownload = false
    @State private var alertMessage: String?
    @State private var showAlert = false
    @State private var avatarViewerState: AvatarViewerState?
    @State private var shareItems: [Any] = []
    @State private var showShareSheet = false

    private let profileService = ProfileService()
    private let descriptionLimit = 1024
    private let nameLimit = 32

    var body: some View {
        ZStack {
            Form {
                Section {
                    HStack {
                        Spacer()
                        VStack {
                            if let image = avatarImage {
                                Image(uiImage: image)
                                    .resizable()
                                    .scaledToFill()
                                    .frame(width: 120, height: 120)
                                    .clipShape(Circle())
                                    .contentShape(Rectangle())
                                    .onTapGesture {
                                        presentAvatarViewer()
                                    }
                            } else if let profile = profile,
                                      let fileId = profile.avatars?.current?.fileId,
                                      let url = avatarUrl(for: profile, fileId: fileId) {
                                CachedAvatarView(url: url, fileId: fileId, userId: profile.userId.uuidString) {
                                    avatarPlaceholder
                                }
                                .aspectRatio(contentMode: .fill)
                                .frame(width: 120, height: 120)
                                .clipShape(Circle())
                                .contentShape(Rectangle())
                                .onTapGesture {
                                    presentAvatarViewer()
                                }
                            } else {
                                avatarPlaceholder
                                    .onTapGesture {
                                        presentAvatarViewer()
                                    }
                            }
                            
                            Button("Изменить фото") {
                                showImagePicker = true
                            }
                            .padding(.top, 8)
                        }
                        Spacer()
                    }
                }
                .listRowBackground(Color(UIColor.systemGroupedBackground))

                Section(header: Text("Публичная информация")) {
                    TextField("Отображаемое имя", text: $displayName)
                        .onChange(of: displayName) { newValue in
                            if newValue.count > nameLimit {
                                displayName = String(newValue.prefix(nameLimit))
                            }
                        }
                    
                    VStack(alignment: .leading, spacing: 5) {
                Text("Описание")
                    .font(.caption)
                    .foregroundColor(.secondary)
                TextEditor(text: $description)
                    .frame(height: 150)
                            .onChange(of: description) { newValue in
                                if newValue.count > descriptionLimit {
                                    description = String(newValue.prefix(descriptionLimit))
                                }
                            }
                        HStack {
                            Spacer()
                            Text("\(description.count) / \(descriptionLimit)")
                                .font(.caption)
                                .foregroundColor(description.count > descriptionLimit ? .red : .secondary)
                        }
                    }
                }

                Button(action: {
                    Task {
                        await applyProfileChanges()
                    }
                }) {
                    if isSaving {
                        ProgressView()
                            .frame(maxWidth: .infinity, alignment: .center)
                    } else {
                        Text("Применить")
                            .frame(maxWidth: .infinity, alignment: .center)
                    }
                }
                .disabled(!hasProfileChanges || isBusy)
            }
            .navigationTitle("Профиль")
            .onAppear(perform: loadProfile)
            .sheet(isPresented: $showImagePicker) {
                ImagePicker(image: $avatarImage, allowsEditing: true)
            }
            .onChange(of: avatarImage) { newValue in
                guard let image = newValue else { return }
                Task {
                    await uploadAvatarImage(image)
                }
            }
            .alert("Ошибка", isPresented: $showAlert, presenting: alertMessage) { _ in
                Button("OK") {}
            } message: { message in
                Text(message)
            }
            .fullScreenCover(item: $avatarViewerState) { state in
                AvatarViewerView(
                    state: state,
                    onClose: { avatarViewerState = nil },
                    onDownload: { handleAvatarDownload(for: state) },
                    onDelete: handleAvatarDeletion
                )
            }
            .sheet(isPresented: $showShareSheet) {
                ActivityView(activityItems: shareItems)
            }
            
            if isBusy {
                Color.black.opacity(0.4).ignoresSafeArea()
                ProgressView(busyMessage)
                    .padding()
                    .background(Color.secondary.colorInvert())
                    .cornerRadius(10)
            }
        }
    }
    
    private var avatarPlaceholder: some View {
        Circle()
            .fill(Color.secondary.opacity(0.2))
            .frame(width: 120, height: 120)
            .overlay(
                Text(profileInitials)
                    .font(.system(size: 48, weight: .semibold))
                    .foregroundColor(.gray)
            )
    }

    private var profileInitials: String {
        if let initials = initials(from: displayName) {
            return initials
        }
        if let profile = profile,
           let name = profile.fullName?.trimmingCharacters(in: .whitespacesAndNewlines),
           !name.isEmpty,
           let initials = initials(from: name) {
            return initials
        }
        if let username = profile?.login.trimmingCharacters(in: .whitespacesAndNewlines), !username.isEmpty {
            return String(username.prefix(1)).uppercased()
        }
        return "?"
    }
    
    private func avatarUrl(for profile: ProfileDataPayload, fileId: String) -> URL? {
        return URL(string: "\(AppConfig.API_SERVER)/v1/storage/download/avatar/\(profile.userId)?file_id=\(fileId)")
    }
    
    private func loadProfile() {
        isLoading = true
        Task {
            do {
                let profile = try await profileService.fetchMyProfile()
                await MainActor.run {
                    self.updateForm(with: profile)
                    self.isLoading = false
                }
            } catch {
                await MainActor.run {
                    self.alertMessage = error.localizedDescription
                    self.showAlert = true
                    self.isLoading = false
                }
            }
        }
    }

    private var hasProfileChanges: Bool {
        displayName != originalDisplayName || description != originalDescription
    }

    private var isBusy: Bool {
        isLoading || isSaving || isUploadingAvatar || isPreparingDownload
    }

    private var busyMessage: String {
        if isUploadingAvatar {
            return "Обновление аватара..."
        }
        if isPreparingDownload {
            return "Подготовка изображения..."
        }
        if isSaving {
            return "Сохранение..."
        }
        return "Загрузка..."
    }

    @MainActor
    private func applyProfileChanges() async {
        guard !isSaving else { return }
        guard let currentProfile = profile else {
            alertMessage = NSLocalizedString("Профиль пока не загружен. Попробуйте позже.", comment: "Profile not ready error")
            showAlert = true
            return
        }

        isSaving = true

        let request = ProfileUpdateRequestPayload(
            fullName: displayName,
            bio: description,
            profilePermissions: ProfilePermissionsRequestPayload(payload: currentProfile.profilePermissions)
        )

        do {
            _ = try await profileService.updateProfile(request)
            let refreshedProfile = try await profileService.fetchMyProfile()
            updateForm(with: refreshedProfile)
        } catch {
            let message: String
            if let error = error as? LocalizedError, let description = error.errorDescription {
                message = description
            } else {
                message = error.localizedDescription
            }
            alertMessage = message
            showAlert = true
        }

        isSaving = false
    }

    @MainActor
    private func uploadAvatarImage(_ image: UIImage) async {
        guard !isUploadingAvatar else { return }
        isUploadingAvatar = true
        defer { isUploadingAvatar = false }

        do {
            _ = try await profileService.uploadAvatar(image: image)
            let refreshedProfile = try await profileService.fetchMyProfile()
            updateFormPreservingFields(profile: refreshedProfile)
            avatarImage = nil
        } catch {
            let message: String
            if let error = error as? LocalizedError, let description = error.errorDescription {
                message = description
            } else {
                message = error.localizedDescription
            }
            alertMessage = message
            showAlert = true
        }
    }

    @MainActor
    private func updateForm(with profile: ProfileDataPayload) {
        self.profile = profile
        applyProfileTexts(from: profile)
    }

    @MainActor
    private func updateFormPreservingFields(profile: ProfileDataPayload) {
        self.profile = profile
        if !hasProfileChanges {
            applyProfileTexts(from: profile)
        }
    }

    @MainActor
    private func applyProfileTexts(from profile: ProfileDataPayload) {
        let loadedName = profile.fullName ?? ""
        let loadedBio = profile.bio ?? ""
        self.displayName = loadedName
        self.description = loadedBio
        self.originalDisplayName = loadedName
        self.originalDescription = loadedBio
    }

    private func presentAvatarViewer() {
        if let image = avatarImage {
            avatarViewerState = AvatarViewerState(
                source: .local(image),
                intrinsicSize: image.size
            )
            return
        }

        guard let profile,
              let fileId = profile.avatars?.current?.fileId,
              let url = avatarUrl(for: profile, fileId: fileId) else { return }
        let intrinsicSize: CGSize?
        if let width = profile.avatars?.current?.width,
           let height = profile.avatars?.current?.height,
           width > 0,
           height > 0 {
            intrinsicSize = CGSize(width: CGFloat(width), height: CGFloat(height))
        } else {
            intrinsicSize = nil
        }
        avatarViewerState = AvatarViewerState(
            source: .remote(url: url, fileId: fileId, userId: profile.userId.uuidString),
            intrinsicSize: intrinsicSize
        )
    }

    private func handleAvatarDownload(for state: AvatarViewerState) {
        guard !isPreparingDownload else { return }
        isPreparingDownload = true

        Task {
            do {
                let image = try await resolveImage(for: state)
                await MainActor.run {
                    shareItems = [image]
                    showShareSheet = true
                }
            } catch {
                await MainActor.run {
                    alertMessage = error.localizedDescription
                    showAlert = true
                }
            }

            await MainActor.run {
                isPreparingDownload = false
            }
        }
    }

    private func handleAvatarDeletion() {
        alertMessage = NSLocalizedString("Удаление аватара пока недоступно.", comment: "Avatar delete placeholder")
        showAlert = true
    }

    private func resolveImage(for state: AvatarViewerState) async throws -> UIImage {
        switch state.source {
        case .local(let image):
            return image
        case .remote(let url, let fileId, let userId):
            if let cached = AvatarCacheService.shared.getImage(forKey: fileId, userId: userId) {
                return cached
            }
            let (data, _) = try await URLSession.shared.data(from: url)
            guard let image = UIImage(data: data) else {
                throw AvatarViewerError.imageDecodingFailed
            }
            AvatarCacheService.shared.saveImage(image, forKey: fileId, userId: userId)
            return image
        }
    }
}

private func initials(from text: String) -> String? {
    let components = text
        .split { $0.isWhitespace }
        .filter { !$0.isEmpty }
    let letters = components.prefix(2).compactMap { $0.first }
    guard !letters.isEmpty else { return nil }
    return letters.map { String($0).uppercased() }.joined()
}

struct ImagePicker: UIViewControllerRepresentable {
    @Binding var image: UIImage?
    var allowsEditing: Bool = false
    @Environment(\.presentationMode) private var presentationMode

    func makeUIViewController(context: Context) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.delegate = context.coordinator
        picker.allowsEditing = allowsEditing
        return picker
    }

    func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
        let parent: ImagePicker

        init(_ parent: ImagePicker) {
            self.parent = parent
        }

        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
            if let editedImage = info[.editedImage] as? UIImage {
                parent.image = editedImage
            } else if let uiImage = info[.originalImage] as? UIImage {
                parent.image = uiImage
            }
            parent.presentationMode.wrappedValue.dismiss()
        }
    }
}

struct AvatarViewerState: Identifiable {
    enum Source {
        case local(UIImage)
        case remote(url: URL, fileId: String, userId: String)
    }

    let id = UUID()
    let source: Source
    let intrinsicSize: CGSize?
}

enum AvatarViewerError: LocalizedError {
    case imageDecodingFailed

    var errorDescription: String? {
        switch self {
        case .imageDecodingFailed:
            return NSLocalizedString("Не удалось подготовить изображение.", comment: "Avatar decoding error")
        }
    }
}

struct AvatarViewerView: View {
    let state: AvatarViewerState
    let onClose: () -> Void
    let onDownload: () -> Void
    let onDelete: () -> Void

    @State private var scale: CGFloat = 1.0
    @State private var baseScale: CGFloat = 1.0
    @State private var panOffset: CGSize = .zero
    @State private var storedPanOffset: CGSize = .zero
    @State private var dismissOffset: CGSize = .zero
    @State private var dragMode: DragMode?
    @State private var containerSize: CGSize = .zero
    @State private var loadedImageSize: CGSize?

    private enum DragMode {
        case vertical
        case horizontal
    }

    private var currentOffset: CGSize {
        scale > 1.05 ? panOffset : dismissOffset
    }

    private var dragProgress: CGFloat {
        guard scale <= 1.05 else { return 0 }
        let progress = min(1, abs(dismissOffset.height) / 220)
        return progress
    }

    private var backgroundOpacity: Double {
        Double(1 - dragProgress * 0.6)
    }

    private var overlayOpacity: Double {
        Double(1 - dragProgress * 0.8)
    }

    private var effectiveImageSize: CGSize? {
        loadedImageSize ?? state.intrinsicSize
    }

    var body: some View {
        ZStack {
            Color.black.opacity(backgroundOpacity).ignoresSafeArea()

            zoomableContent

            topOverlay
                .opacity(overlayOpacity)
        }
    }

    private var topOverlay: some View {
        VStack(spacing: 8) {
            HStack {
                Button(action: onClose) {
                    Image(systemName: "xmark")
                        .imageScale(.large)
                }
                .tint(.white)

                Spacer()

                Text("1 из 1")
                    .font(.subheadline.weight(.semibold))
                    .foregroundColor(.white.opacity(0.8))
                
                Spacer()
                
                Menu {
                    Button(action: onDownload) {
                        Label(NSLocalizedString("Скачать", comment: "Avatar download"), systemImage: "square.and.arrow.down")
                    }
                    Button(role: .destructive, action: onDelete) {
                        Label(NSLocalizedString("Удалить фото", comment: "Avatar delete"), systemImage: "trash")
                    }
                } label: {
                    Image(systemName: "ellipsis.circle")
                        .imageScale(.large)
                }
                .tint(.white)
            }
            .padding(.horizontal)
            .padding(.top, 24)

            Spacer()
        }
    }

    @ViewBuilder
    private var zoomableContent: some View {
        GeometryReader { proxy in
            let size = proxy.size
            Color.clear
                .onAppear { containerSize = size }
                .onChange(of: size) { newValue in
                    containerSize = newValue
                }
                .overlay {
                    content(for: size)
                }
        }
    }

    @ViewBuilder
    private func content(for size: CGSize) -> some View {
        switch state.source {
        case .local(let image):
            zoomableImage(Image(uiImage: image))
                .onAppear {
                    loadedImageSize = image.size
                }
        case .remote(let url, let fileId, let userId):
            RemoteZoomableImage(url: url, fileId: fileId, userId: userId) { uiImage in
                loadedImageSize = uiImage.size
            }
            .offset(currentOffset)
            .scaleEffect(scale, anchor: .center)
            .gesture(dragGesture)
            .simultaneousGesture(magnificationGesture)
        }
    }

    private func zoomableImage(_ image: Image) -> some View {
        image
            .resizable()
            .scaledToFit()
            .offset(currentOffset)
            .scaleEffect(scale, anchor: .center)
            .gesture(dragGesture)
            .simultaneousGesture(magnificationGesture)
            .frame(maxWidth: .infinity, maxHeight: .infinity)
    }

    private var dragGesture: some Gesture {
        DragGesture()
            .onChanged { value in
                if scale > 1.05 {
                    dismissOffset = .zero
                    let adjustedTranslation = CGSize(
                        width: value.translation.width / scale,
                        height: value.translation.height / scale
                    )
                    panOffset = CGSize(
                        width: storedPanOffset.width + adjustedTranslation.width,
                        height: storedPanOffset.height + adjustedTranslation.height
                    )
                    panOffset = clampedOffset(panOffset)
                } else {
                    if dragMode == nil {
                        if abs(value.translation.height) > abs(value.translation.width) {
                            dragMode = .vertical
                        } else {
                            dragMode = .horizontal
                        }
                    }

                    switch dragMode {
                    case .horizontal:
                        let limitedWidth = min(max(value.translation.width, -80), 80)
                        dismissOffset = CGSize(width: limitedWidth, height: 0)
                    case .vertical, .none:
                        dismissOffset = CGSize(width: 0, height: value.translation.height)
                    }
                }
            }
            .onEnded { value in
                if scale > 1.05 {
                    let adjustedTranslation = CGSize(
                        width: value.translation.width / scale,
                        height: value.translation.height / scale
                    )
                    var newOffset = CGSize(
                        width: storedPanOffset.width + adjustedTranslation.width,
                        height: storedPanOffset.height + adjustedTranslation.height
                    )
                    newOffset = clampedOffset(newOffset)
                    storedPanOffset = newOffset
                } else {
                    if abs(value.translation.height) > 120 {
                        onClose()
                    } else {
                        withAnimation(.spring()) {
                            dismissOffset = .zero
                        }
                    }
                    dragMode = nil
                }
            }
    }

    private var magnificationGesture: some Gesture {
        MagnificationGesture()
            .onChanged { value in
                let newScale = baseScale * value
                scale = min(max(newScale, 1), 4)
            }
            .onEnded { _ in
                baseScale = scale
                if baseScale <= 1.02 {
                    baseScale = 1
                    withAnimation(.spring()) {
                        scale = 1
                        storedPanOffset = .zero
                        panOffset = .zero
                        dismissOffset = .zero
                    }
                }
            }
    }

    private func clampedOffset(_ offset: CGSize) -> CGSize {
        guard scale > 1.01,
              containerSize != .zero else { return offset }

        let fittedSize = fittedContentSize(in: containerSize)
        let scaledWidth = fittedSize.width * scale
        let scaledHeight = fittedSize.height * scale
        let maxX = max(0, (scaledWidth - containerSize.width) / 2)
        let maxY = max(0, (scaledHeight - containerSize.height) / 2)

        let clampedX = max(-maxX, min(offset.width, maxX))
        let clampedY = max(-maxY, min(offset.height, maxY))
        return CGSize(width: clampedX, height: clampedY)
    }

    private func fittedContentSize(in container: CGSize) -> CGSize {
        guard let imageSize = effectiveImageSize,
              imageSize.width > 0,
              imageSize.height > 0 else {
            return container
        }

        let widthRatio = container.width / imageSize.width
        let heightRatio = container.height / imageSize.height
        let ratio = min(widthRatio, heightRatio)
        return CGSize(width: imageSize.width * ratio, height: imageSize.height * ratio)
    }
}

private struct RemoteZoomableImage: View {
    @StateObject private var loader: ImageLoader
    let onImageLoaded: (UIImage) -> Void

    init(url: URL, fileId: String, userId: String, onImageLoaded: @escaping (UIImage) -> Void) {
        _loader = StateObject(wrappedValue: ImageLoader(url: url, fileId: fileId, userId: userId))
        self.onImageLoaded = onImageLoaded
    }

    var body: some View {
        Group {
            if let image = loader.image {
                Image(uiImage: image)
                    .resizable()
                    .scaledToFit()
                    .onAppear {
                        onImageLoaded(image)
                    }
            } else {
                ProgressView()
                    .progressViewStyle(.circular)
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .onAppear(perform: loader.load)
    }
}

struct ActivityView: UIViewControllerRepresentable {
    let activityItems: [Any]

    func makeUIViewController(context: Context) -> UIActivityViewController {
        UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
    }

    func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {}
}
