import DesignSystem
import Env
import Models
import Observation
import SafariServices
import SwiftUI

extension View {
  @MainActor func withSafariRouter() -> some View {
    modifier(SafariRouter())
  }
}

@MainActor
private struct SafariRouter: ViewModifier {
  @Environment(Theme.self) private var theme
  @Environment(UserPreferences.self) private var preferences
  @Environment(RouterPath.self) private var routerPath

  #if !os(visionOS)
  @State private var safariManager = InAppSafariManager()
  #endif

  func body(content: Content) -> some View {
    content
      .environment(\.openURL, OpenURLAction { url in
        // Open internal URL.
        routerPath.handle(url: url)
      })
      .onOpenURL { url in
        // Open external URL (from icecubesapp://)
        let urlString = url.absoluteString.replacingOccurrences(of: AppInfo.scheme, with: "https://")
        guard let url = URL(string: urlString), url.host != nil else { return }
        _ = routerPath.handle(url: url)
      }
      .onAppear {
        routerPath.urlHandler = { url in
          if url.absoluteString.contains("@twitter.com"), url.absoluteString.hasPrefix("mailto:") {
            let username = url.absoluteString
              .replacingOccurrences(of: "@twitter.com", with: "")
              .replacingOccurrences(of: "mailto:", with: "")
            let twitterLink = "https://twitter.com/\(username)"
            if let url = URL(string: twitterLink) {
              UIApplication.shared.open(url)
              return .handled
            }
          }
          #if !targetEnvironment(macCatalyst)
            guard preferences.preferredBrowser == .inAppSafari else { return .systemAction }
          #endif
          // SFSafariViewController only supports initial URLs with http:// or https:// schemes.
          guard let scheme = url.scheme, ["https", "http"].contains(scheme.lowercased()) else {
            return .systemAction
          }
          #if os(visionOS)
          return .systemAction
          #else
          return safariManager.open(url)
          #endif
        }
      }
      #if !os(visionOS)
      .background {
        WindowReader { window in
          safariManager.windowScene = window.windowScene
        }
      }
      #endif
  }
}

#if !os(visionOS)
@MainActor
@Observable private class InAppSafariManager: NSObject, SFSafariViewControllerDelegate {
  var windowScene: UIWindowScene?
  let viewController: UIViewController = .init()
  var window: UIWindow?

  @MainActor
  func open(_ url: URL) -> OpenURLAction.Result {
    guard let windowScene else { return .systemAction }

    window = setupWindow(windowScene: windowScene)

    let configuration = SFSafariViewController.Configuration()
    configuration.entersReaderIfAvailable = UserPreferences.shared.inAppBrowserReaderView

    let safari = SFSafariViewController(url: url, configuration: configuration)
    safari.preferredBarTintColor = UIColor(Theme.shared.primaryBackgroundColor)
    safari.preferredControlTintColor = UIColor(Theme.shared.tintColor)
    safari.delegate = self

    DispatchQueue.main.async { [weak self] in
      self?.viewController.present(safari, animated: true)
    }

    return .handled
  }

  func setupWindow(windowScene: UIWindowScene) -> UIWindow {
    let window = window ?? UIWindow(windowScene: windowScene)

    window.rootViewController = viewController
    window.makeKeyAndVisible()

    switch Theme.shared.selectedScheme {
    case .dark:
      window.overrideUserInterfaceStyle = .dark
    case .light:
      window.overrideUserInterfaceStyle = .light
    }

    self.window = window
    return window
  }

  nonisolated func safariViewControllerDidFinish(_: SFSafariViewController) {
    Task { @MainActor in
      window?.resignKey()
      window?.isHidden = false
      window = nil
    }
  }
}
#endif

private struct WindowReader: UIViewRepresentable {
  var onUpdate: (UIWindow) -> Void

  func makeUIView(context _: Context) -> InjectView {
    InjectView(onUpdate: onUpdate)
  }

  func updateUIView(_: InjectView, context _: Context) {}

  class InjectView: UIView {
    var onUpdate: (UIWindow) -> Void

    init(onUpdate: @escaping (UIWindow) -> Void) {
      self.onUpdate = onUpdate
      super.init(frame: .zero)
      isHidden = true
      isUserInteractionEnabled = false
    }

    @available(*, unavailable)
    required init?(coder _: NSCoder) {
      fatalError("init(coder:) has not been implemented")
    }

    override func willMove(toWindow newWindow: UIWindow?) {
      super.willMove(toWindow: newWindow)

      if let window = newWindow {
        onUpdate(window)
      } else {
        DispatchQueue.main.async { [weak self] in
          if let window = self?.window {
            self?.onUpdate(window)
          }
        }
      }
    }
  }
}
