import Combine
import SwiftUI

@Observable public class Theme {
  class ThemeStorage {
    enum ThemeKey: String {
      case colorScheme, tint, label, primaryBackground, secondaryBackground
      case avatarPosition2, avatarShape2, statusActionsDisplay, statusDisplayStyle
      case selectedSet, selectedScheme
      case followSystemColorSchme
      case displayFullUsernameTimeline
      case lineSpacing
      case statusActionSecondary
    }

    @AppStorage("is_previously_set") public var isThemePreviouslySet: Bool = false
    @AppStorage(ThemeKey.selectedScheme.rawValue) public var selectedScheme: ColorScheme = .dark
    @AppStorage(ThemeKey.tint.rawValue) public var tintColor: Color = .black
    @AppStorage(ThemeKey.primaryBackground.rawValue) public var primaryBackgroundColor: Color = .white
    @AppStorage(ThemeKey.secondaryBackground.rawValue) public var secondaryBackgroundColor: Color = .gray
    @AppStorage(ThemeKey.label.rawValue) public var labelColor: Color = .black
    @AppStorage(ThemeKey.avatarPosition2.rawValue) var avatarPosition: AvatarPosition = .leading
    @AppStorage(ThemeKey.avatarShape2.rawValue) var avatarShape: AvatarShape = .circle
    @AppStorage(ThemeKey.selectedSet.rawValue) var storedSet: ColorSetName = .iceCubeDark
    @AppStorage(ThemeKey.statusActionsDisplay.rawValue) public var statusActionsDisplay: StatusActionsDisplay = .full
    @AppStorage(ThemeKey.statusDisplayStyle.rawValue) public var statusDisplayStyle: StatusDisplayStyle = .large
    @AppStorage(ThemeKey.followSystemColorSchme.rawValue) public var followSystemColorScheme: Bool = true
    @AppStorage(ThemeKey.displayFullUsernameTimeline.rawValue) public var displayFullUsername: Bool = false
    @AppStorage(ThemeKey.lineSpacing.rawValue) public var lineSpacing: Double = 1.2
    @AppStorage(ThemeKey.statusActionSecondary.rawValue) public var statusActionSecondary: StatusActionSecondary = .share
    @AppStorage("font_size_scale") public var fontSizeScale: Double = 1
    @AppStorage("chosen_font") public var chosenFontData: Data?

    init() {}
  }

  public enum FontState: Int, CaseIterable {
    case system
    case openDyslexic
    case hyperLegible
    case SFRounded
    case custom

    public var title: LocalizedStringKey {
      switch self {
      case .system:
        "settings.display.font.system"
      case .openDyslexic:
        "Open Dyslexic"
      case .hyperLegible:
        "Hyper Legible"
      case .SFRounded:
        "SF Rounded"
      case .custom:
        "settings.display.font.custom"
      }
    }
  }

  public enum AvatarPosition: String, CaseIterable {
    case leading, top

    public var description: LocalizedStringKey {
      switch self {
      case .leading:
        "enum.avatar-position.leading"
      case .top:
        "enum.avatar-position.top"
      }
    }
  }
  
  public enum StatusActionSecondary: String, CaseIterable {
    case share, bookmark
    
    public var description: LocalizedStringKey {
      switch self {
      case .share:
        "status.action.share-title"
      case .bookmark:
        "status.action.bookmark"
      }
    }
  }

  public enum AvatarShape: String, CaseIterable {
    case circle, rounded

    public var description: LocalizedStringKey {
      switch self {
      case .circle:
        "enum.avatar-shape.circle"
      case .rounded:
        "enum.avatar-shape.rounded"
      }
    }
  }

  public enum StatusActionsDisplay: String, CaseIterable {
    case full, discret, none

    public var description: LocalizedStringKey {
      switch self {
      case .full:
        "enum.status-actions-display.all"
      case .discret:
        "enum.status-actions-display.only-buttons"
      case .none:
        "enum.status-actions-display.no-buttons"
      }
    }
  }

  public enum StatusDisplayStyle: String, CaseIterable {
    case large, medium, compact

    public var description: LocalizedStringKey {
      switch self {
      case .large:
        "enum.status-display-style.large"
      case .medium:
        "enum.status-display-style.medium"
      case .compact:
        "enum.status-display-style.compact"
      }
    }
  }

  private var _cachedChoosenFont: UIFont?
  public var chosenFont: UIFont? {
    get {
      if let _cachedChoosenFont {
        return _cachedChoosenFont
      }
      guard let chosenFontData,
            let font = try? NSKeyedUnarchiver.unarchivedObject(ofClass: UIFont.self, from: chosenFontData) else { return nil }

      _cachedChoosenFont = font
      return font
    }
    set {
      if let font = newValue,
         let data = try? NSKeyedArchiver.archivedData(withRootObject: font, requiringSecureCoding: false)
      {
        chosenFontData = data
      } else {
        chosenFontData = nil
      }
      _cachedChoosenFont = nil
    }
  }

  let themeStorage = ThemeStorage()

  public var isThemePreviouslySet: Bool {
    didSet {
      themeStorage.isThemePreviouslySet = isThemePreviouslySet
    }
  }

  public var selectedScheme: ColorScheme {
    didSet {
      themeStorage.selectedScheme = selectedScheme
    }
  }

  public var tintColor: Color {
    didSet {
      themeStorage.tintColor = tintColor
    }
  }

  public var primaryBackgroundColor: Color {
    didSet {
      themeStorage.primaryBackgroundColor = primaryBackgroundColor
    }
  }

  public var secondaryBackgroundColor: Color {
    didSet {
      themeStorage.secondaryBackgroundColor = secondaryBackgroundColor
    }
  }

  public var labelColor: Color {
    didSet {
      themeStorage.labelColor = labelColor
    }
  }

  public var avatarPosition: AvatarPosition {
    didSet {
      themeStorage.avatarPosition = avatarPosition
    }
  }

  public var avatarShape: AvatarShape {
    didSet {
      themeStorage.avatarShape = avatarShape
    }
  }

  private var storedSet: ColorSetName {
    didSet {
      themeStorage.storedSet = storedSet
    }
  }

  public var statusActionsDisplay: StatusActionsDisplay {
    didSet {
      themeStorage.statusActionsDisplay = statusActionsDisplay
    }
  }

  public var statusDisplayStyle: StatusDisplayStyle {
    didSet {
      themeStorage.statusDisplayStyle = statusDisplayStyle
    }
  }
  
  public var statusActionSecondary: StatusActionSecondary {
    didSet {
      themeStorage.statusActionSecondary = statusActionSecondary
    }
  }

  public var followSystemColorScheme: Bool {
    didSet {
      themeStorage.followSystemColorScheme = followSystemColorScheme
    }
  }

  public var displayFullUsername: Bool {
    didSet {
      themeStorage.displayFullUsername = displayFullUsername
    }
  }

  public var lineSpacing: Double {
    didSet {
      themeStorage.lineSpacing = lineSpacing
    }
  }

  public var fontSizeScale: Double {
    didSet {
      themeStorage.fontSizeScale = fontSizeScale
    }
  }

  public private(set) var chosenFontData: Data? {
    didSet {
      themeStorage.chosenFontData = chosenFontData
    }
  }

  public var selectedSet: ColorSetName = .iceCubeDark

  public static let shared = Theme()

  private init() {
    isThemePreviouslySet = themeStorage.isThemePreviouslySet
    selectedScheme = themeStorage.selectedScheme
    tintColor = themeStorage.tintColor
    primaryBackgroundColor = themeStorage.primaryBackgroundColor
    secondaryBackgroundColor = themeStorage.secondaryBackgroundColor
    labelColor = themeStorage.labelColor
    avatarPosition = themeStorage.avatarPosition
    avatarShape = themeStorage.avatarShape
    storedSet = themeStorage.storedSet
    statusActionsDisplay = themeStorage.statusActionsDisplay
    statusDisplayStyle = themeStorage.statusDisplayStyle
    followSystemColorScheme = themeStorage.followSystemColorScheme
    displayFullUsername = themeStorage.displayFullUsername
    lineSpacing = themeStorage.lineSpacing
    fontSizeScale = themeStorage.fontSizeScale
    chosenFontData = themeStorage.chosenFontData
    statusActionSecondary = themeStorage.statusActionSecondary
    selectedSet = storedSet
  }

  public static var allColorSet: [ColorSet] {
    [
      IceCubeDark(),
      IceCubeLight(),
      IceCubeNeonDark(),
      IceCubeNeonLight(),
      DesertDark(),
      DesertLight(),
      NemesisDark(),
      NemesisLight(),
      MediumLight(),
      MediumDark(),
      ConstellationLight(),
      ConstellationDark(),
      ThreadsLight(),
      ThreadsDark()
    ]
  }

  public func applySet(set: ColorSetName) {
    selectedSet = set
    setColor(withName: set)
  }

  public func setColor(withName name: ColorSetName) {
    let colorSet = Theme.allColorSet.filter { $0.name == name }.first ?? IceCubeDark()
    selectedScheme = colorSet.scheme
    tintColor = colorSet.tintColor
    primaryBackgroundColor = colorSet.primaryBackgroundColor
    secondaryBackgroundColor = colorSet.secondaryBackgroundColor
    labelColor = colorSet.labelColor
    storedSet = name
  }
}
