import DesignSystem
import Env
import Models
import NukeUI
import PhotosUI
import SwiftUI

struct StatusEditorAccessoryView: View {
  @EnvironmentObject private var preferences: UserPreferences
  @EnvironmentObject private var theme: Theme
  @EnvironmentObject private var currentInstance: CurrentInstance

  @FocusState<Bool>.Binding var isSpoilerTextFocused: Bool
  @ObservedObject var viewModel: StatusEditorViewModel

  @State private var isDraftsSheetDisplayed: Bool = false
  @State private var isLanguageSheetDisplayed: Bool = false
  @State private var isCustomEmojisSheetDisplay: Bool = false
  @State private var languageSearch: String = ""
  @State private var isLoadingAIRequest: Bool = false

  var body: some View {
    VStack(spacing: 0) {
      Divider()
      HStack {
        ScrollView(.horizontal) {
          HStack(alignment: .center, spacing: 16) {
            PhotosPicker(selection: $viewModel.selectedMedias,
                         matching: .any(of: [.images, .videos])) {
              if viewModel.isMediasLoading {
                ProgressView()
              } else {
                Image(systemName: "photo.fill.on.rectangle.fill")
              }
            }
            .disabled(viewModel.showPoll)

            Button {
              withAnimation {
                viewModel.showPoll.toggle()
              }
            } label: {
              Image(systemName: "chart.bar")
            }
            .disabled(viewModel.shouldDisablePollButton)

            Button {
              withAnimation {
                viewModel.spoilerOn.toggle()
              }
              isSpoilerTextFocused.toggle()
            } label: {
              Image(systemName: viewModel.spoilerOn ? "exclamationmark.triangle.fill" : "exclamationmark.triangle")
            }

            if !viewModel.mode.isInShareExtension {
              Button {
                isDraftsSheetDisplayed = true
              } label: {
                Image(systemName: "archivebox")
              }
            }

            if !viewModel.customEmojis.isEmpty {
              Button {
                isCustomEmojisSheetDisplay = true
              } label: {
                Image(systemName: "face.smiling.inverse")
              }
            }

            Button {
              isLanguageSheetDisplayed.toggle()
            } label: {
              if let language = viewModel.selectedLanguage {
                Text(language.uppercased())
              } else {
                Image(systemName: "globe")
              }
            }

            if preferences.isOpenAIEnabled {
              AIMenu.disabled(!viewModel.canPost)
            }
          }
          .padding(.horizontal, .layoutPadding)
        }
        Spacer()
        characterCountView
          .padding(.trailing, .layoutPadding)
      }
      .frame(height: 20)
      .padding(.vertical, 12)
      .background(.ultraThinMaterial)
    }
    .sheet(isPresented: $isDraftsSheetDisplayed) {
      draftsSheetView
    }
    .sheet(isPresented: $isLanguageSheetDisplayed) {
      languageSheetView
    }
    .sheet(isPresented: $isCustomEmojisSheetDisplay) {
      customEmojisSheet
    }
    .onAppear {
      viewModel.setInitialLanguageSelection(preference: preferences.serverPreferences?.postLanguage)
    }
  }

  @ViewBuilder
  private func languageTextView(isoCode: String, nativeName: String?, name: String?) -> some View {
    if let nativeName = nativeName, let name = name {
      Text("\(nativeName) (\(name))")
    } else {
      Text(isoCode.uppercased())
    }
  }

  private var AIMenu: some View {
    Menu {
      ForEach(StatusEditorAIPrompts.allCases, id: \.self) { prompt in
        Button {
          Task {
            isLoadingAIRequest = true
            await viewModel.runOpenAI(prompt: prompt.toRequestPrompt(text: viewModel.statusText.string))
            isLoadingAIRequest = false
          }
        } label: {
          prompt.label
        }
      }
      if let backup = viewModel.backupStatusText {
        Button {
          viewModel.replaceTextWith(text: backup.string)
          viewModel.backupStatusText = nil
        } label: {
          Label("status.editor.restore-previous", systemImage: "arrow.uturn.right")
        }
      }
    } label: {
      if isLoadingAIRequest {
        ProgressView()
      } else {
        Image(systemName: "faxmachine")
      }
    }
  }

  private var languageSheetView: some View {
    NavigationStack {
      List {
        if languageSearch.isEmpty {
          if !recentlyUsedLanguages.isEmpty {
            Section("Recently Used") {
              languageSheetSection(languages: recentlyUsedLanguages)
            }
          }
          Section {
            languageSheetSection(languages: otherLanguages)
          }
        } else {
          languageSheetSection(languages: languageSearchResult(query: languageSearch))
        }
      }
      .searchable(text: $languageSearch)
      .toolbar {
        ToolbarItem(placement: .navigationBarLeading) {
          Button("action.cancel", action: { isLanguageSheetDisplayed = false })
        }
      }
      .navigationTitle("status.editor.language-select.navigation-title")
      .navigationBarTitleDisplayMode(.inline)
      .scrollContentBackground(.hidden)
      .background(theme.secondaryBackgroundColor)
    }
  }

  private func languageSheetSection(languages: [Language]) -> some View {
    ForEach(languages) { language in
      HStack {
        languageTextView(
          isoCode: language.isoCode,
          nativeName: language.nativeName,
          name: language.localizedName
        ).tag(language.isoCode)
        Spacer()
        if language.isoCode == viewModel.selectedLanguage {
          Image(systemName: "checkmark")
        }
      }
      .listRowBackground(theme.primaryBackgroundColor)
      .contentShape(Rectangle())
      .onTapGesture {
        viewModel.selectedLanguage = language.isoCode
        viewModel.hasExplicitlySelectedLanguage = true
        isLanguageSheetDisplayed = false
      }
    }
  }

  private var draftsSheetView: some View {
    NavigationStack {
      List {
        ForEach(preferences.draftsPosts, id: \.self) { draft in
          Text(draft)
            .lineLimit(3)
            .listRowBackground(theme.primaryBackgroundColor)
            .onTapGesture {
              viewModel.insertStatusText(text: draft)
              isDraftsSheetDisplayed = false
            }
        }
        .onDelete { indexes in
          if let index = indexes.first {
            preferences.draftsPosts.remove(at: index)
          }
        }
      }
      .toolbar {
        ToolbarItem(placement: .navigationBarLeading) {
          Button("action.cancel", action: { isDraftsSheetDisplayed = false })
        }
      }
      .scrollContentBackground(.hidden)
      .background(theme.secondaryBackgroundColor)
      .navigationTitle("status.editor.drafts.navigation-title")
      .navigationBarTitleDisplayMode(.inline)
    }
    .presentationDetents([.medium])
  }

  private var customEmojisSheet: some View {
    NavigationStack {
      ScrollView {
        LazyVGrid(columns: [GridItem(.adaptive(minimum: 40))], spacing: 9) {
          ForEach(viewModel.customEmojis) { emoji in
            LazyImage(url: emoji.url) { state in
              if let image = state.image {
                image
                  .resizingMode(.aspectFit)
                  .frame(width: 40, height: 40)
              } else if state.isLoading {
                Rectangle()
                  .fill(Color.gray)
                  .frame(width: 40, height: 40)
                  .shimmering()
              }
            }
            .onTapGesture {
              viewModel.insertStatusText(text: " :\(emoji.shortcode): ")
              isCustomEmojisSheetDisplay = false
            }
          }
        }.padding(.horizontal)
      }
      .scrollContentBackground(.hidden)
      .background(theme.primaryBackgroundColor)
      .navigationTitle("Custom Emojis")
      .navigationBarTitleDisplayMode(.inline)
    }
    .presentationDetents([.medium])
  }

  private var characterCountView: some View {
    Text("\((currentInstance.instance?.configuration?.statuses.maxCharacters ?? 500) + viewModel.statusTextCharacterLength)")
      .foregroundColor(.gray)
      .font(.scaledCallout)
  }

  private struct Language: Identifiable, Equatable {
    var id: String { isoCode }

    let isoCode: String
    let nativeName: String?
    let localizedName: String?
  }

  private let allAvailableLanguages: [Language] =
    Locale.LanguageCode.isoLanguageCodes
      .filter { $0.identifier.count == 2 } // Mastodon only supports ISO 639-1 (two-letter) codes
      .map { lang in
        let nativeLocale = Locale(languageComponents: Locale.Language.Components(languageCode: lang))
        return Language(
          isoCode: lang.identifier,
          nativeName: nativeLocale.localizedString(forLanguageCode: lang.identifier)?.capitalized,
          localizedName: Locale.current.localizedString(forLanguageCode: lang.identifier)?.localizedCapitalized
        )
      }

  private var recentlyUsedLanguages: [Language] {
    preferences.recentlyUsedLanguages.compactMap { isoCode in
      allAvailableLanguages.first { $0.isoCode == isoCode }
    }
  }

  private var otherLanguages: [Language] {
    allAvailableLanguages.filter { !preferences.recentlyUsedLanguages.contains($0.isoCode) }
  }

  private func languageSearchResult(query: String) -> [Language] {
    allAvailableLanguages.filter { language in
      guard !languageSearch.isEmpty else {
        return true
      }
      return language.nativeName?.lowercased().hasPrefix(query.lowercased()) == true
        || language.localizedName?.lowercased().hasPrefix(query.lowercased()) == true
    }
  }
}
