// generated by bun run generate from the SwiftUI SDK and codegen/catalog.ts.
// edit the generator or catalog, then regenerate.
import SwiftUI
import UIKit
import Charts

private final class ChartModel: ObservableObject {
  @Published var data: String = "[]"
  @Published var type: String = "line"
  @Published var showGrid: Bool = true
  @Published var animate: Bool = true
  @Published var showLegend: Bool = false
  @Published var referenceLines: String = "[]"
  @Published var lineStyle: String = "{}"
  @Published var pointStyle: String = "{}"
  @Published var areaStyle: String = "{}"
  @Published var barStyle: String = "{}"
  @Published var pieStyle: String = "{}"
  @Published var rectangleStyle: String = "{}"
  @Published var ruleStyle: String = "{}"
  @Published var accessibility = OneNativeAccessibility()
  @Published var swiftStyle = OneNativeStyle()
  var active = false
  var onSDKEvent: ((String, String) -> Void)?
  func emitSDKEvent(_ name: String, _ value: String) { if active { onSDKEvent?(name, value) } }

}
@objcMembers public final class OneNativeChartView: UIView, OneNativeComposable {
  public var onSDKEvent: ((String, String) -> Void)?

  private var model = ChartModel()
  private var controller: OneNativeHostingController<OneNativeStandalone<ChartContent>>?
  public override init(frame: CGRect) { super.init(frame: frame) }
  required init?(coder: NSCoder) { fatalError("init(coder:) is unavailable") }
  public func configureAccessibility(_ label: String, hint: String, value: String, identifier: String) {
    let next = OneNativeAccessibility(label: label, hint: hint, value: value, identifier: identifier)
    if model.accessibility != next { model.accessibility = next }
  }
  public func configureStyle(_ style: [String: Any]) {
    let next = OneNativeStyle(dictionary: style)
    if model.swiftStyle != next {
      let previousRows = model.swiftStyle.sdkModifiers.filter { $0.first?.hasPrefix("listRow") == true }
      let nextRows = next.sdkModifiers.filter { $0.first?.hasPrefix("listRow") == true }
      model.swiftStyle = next
      if previousRows != nextRows { compositionParent?.refreshRow(for: self) }
    }
  }
  public func configure(_ data: String, type: String, showGrid: Bool, animate: Bool, showLegend: Bool, referenceLines: String, lineStyle: String, pointStyle: String, areaStyle: String, barStyle: String, pieStyle: String, rectangleStyle: String, ruleStyle: String) {
    if model.data != data { model.data = data }
    if model.type != type { model.type = type }
    if model.showGrid != showGrid { model.showGrid = showGrid }
    if model.animate != animate { model.animate = animate }
    if model.showLegend != showLegend { model.showLegend = showLegend }
    if model.referenceLines != referenceLines { model.referenceLines = referenceLines }
    if model.lineStyle != lineStyle { model.lineStyle = lineStyle }
    if model.pointStyle != pointStyle { model.pointStyle = pointStyle }
    if model.areaStyle != areaStyle { model.areaStyle = areaStyle }
    if model.barStyle != barStyle { model.barStyle = barStyle }
    if model.pieStyle != pieStyle { model.pieStyle = pieStyle }
    if model.rectangleStyle != rectangleStyle { model.rectangleStyle = rectangleStyle }
    if model.ruleStyle != ruleStyle { model.ruleStyle = ruleStyle }
  }


  private weak var compositionParent: OneNativeCompositionParent?
  public func compositionContent() -> AnyView { AnyView(ChartContent(model: model)) }
  // composed, activation follows the parent: a subtree mounted before root attachment
  // stays silent until the root attaches.
  public func composeInto(_ parent: OneNativeCompositionParent) {
    controller?.detach(); controller = nil
    compositionParent = parent
    bindCallbacks()
    model.active = parent.compositionActive
  }
  public func decompose() { compositionParent = nil; model.active = false }
  public func propagateActive(_ active: Bool) { model.active = active }
  public override func didMoveToWindow() { super.didMoveToWindow(); updateHost() }
  public override func layoutSubviews() { super.layoutSubviews(); updateHost() }
  private func bindCallbacks() {
    model.onSDKEvent = { [weak self] name, value in self?.onSDKEvent?(name, value) }

  }
  private func updateHost() {
    guard compositionParent == nil else { return }
    model.active = false
    guard window != nil else { controller?.detach(); return }
    if controller == nil {
      bindCallbacks()
      controller = OneNativeHostingController(rootView: OneNativeStandalone(content: ChartContent(model: model)))
    }
    controller?.attach(to: self)
    model.active = controller?.isAttached == true
  }
  public func reset() {
    compositionParent = nil
    model.active = false; model.onSDKEvent = nil
    controller?.detach(); controller = nil; model = ChartModel()
  }
}
private struct ChartContent: View {
  @ObservedObject var model: ChartModel
  var body: some View {
    OneNativeChartSurface(model: model)
      .oneNativeAccessibility(model.accessibility)
      .oneNativeStyle(model.swiftStyle, emit: model.emitSDKEvent)
  }
}
private struct OneNativeChartPoint: Decodable {
  enum X { case label(String), number(Double) }
  let x: X
  let y: Double
  let color: Color?
  private enum CodingKeys: String, CodingKey { case x, y, color }
  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    if let number = try? container.decode(Double.self, forKey: .x) {
      x = .number(number)
    } else {
      x = .label(try container.decode(String.self, forKey: .x))
    }
    y = try container.decode(Double.self, forKey: .y)
    color = try container.decodeIfPresent(UInt32.self, forKey: .color).map(oneNativeChartColor)
  }
  var label: String {
    switch x {
    case .label(let label): return label
    case .number(let number): return String(number)
    }
  }
}

// every chart style type in one shape; chartNative admits only each type's own keys.
private struct OneNativeChartStyle: Decodable {
  var dashArray: [Double]?
  var width: Double?
  var pointStyle: String?
  var pointSize: Double?
  var color: UInt32?
  var cornerRadius: Double?
  var innerRadius: Double?
  var angularInset: Double?
  var lineWidth: Double?
}

// React Native's processed color: 0xAARRGGBB.
private func oneNativeChartColor(_ argb: UInt32) -> Color {
  Color(.sRGB,
    red: Double((argb >> 16) & 0xff) / 255,
    green: Double((argb >> 8) & 0xff) / 255,
    blue: Double(argb & 0xff) / 255,
    opacity: Double((argb >> 24) & 0xff) / 255)
}

private func oneNativeChartDecode<T: Decodable>(_ json: String, _ name: String) -> T {
  guard let data = json.data(using: .utf8), let value = try? JSONDecoder().decode(T.self, from: data) else {
    preconditionFailure("invalid Chart \(name)")
  }
  return value
}

private enum OneNativeChartKind: String { case line, point, bar, area, pie, rectangle }

private func oneNativeChartKind(_ value: String) -> OneNativeChartKind {
  guard let kind = OneNativeChartKind(rawValue: value) else { preconditionFailure("invalid Chart type: \(value)") }
  return kind
}

private func oneNativeChartSymbol(_ style: String?) -> BasicChartSymbolShape {
  switch style ?? "circle" {
  case "circle": return .circle
  case "square": return .square
  case "diamond": return .diamond
  default: preconditionFailure("invalid Chart pointStyle: \(style ?? "")")
  }
}

private struct OneNativeChartSurface: View {
  @ObservedObject var model: ChartModel

  var body: some View {
    let points: [OneNativeChartPoint] = oneNativeChartDecode(model.data, "data")
    let rules: [OneNativeChartPoint] = oneNativeChartDecode(model.referenceLines, "referenceLines")
    let line: OneNativeChartStyle = oneNativeChartDecode(model.lineStyle, "lineStyle")
    let point: OneNativeChartStyle = oneNativeChartDecode(model.pointStyle, "pointStyle")
    let area: OneNativeChartStyle = oneNativeChartDecode(model.areaStyle, "areaStyle")
    let bar: OneNativeChartStyle = oneNativeChartDecode(model.barStyle, "barStyle")
    let pie: OneNativeChartStyle = oneNativeChartDecode(model.pieStyle, "pieStyle")
    let rectangle: OneNativeChartStyle = oneNativeChartDecode(model.rectangleStyle, "rectangleStyle")
    let rule: OneNativeChartStyle = oneNativeChartDecode(model.ruleStyle, "ruleStyle")
    // categorical colors and the legend apply only when no point names its own color.
    let individualColors = points.contains { $0.color != nil }
    let kind = oneNativeChartKind(model.type)
    Chart {
      ForEach(Array(points.enumerated()), id: \.offset) { _, item in
        switch kind {
        case .line:
          switch item.x {
          case .label(let x): lineMark(x, item, line)
          case .number(let x): lineMark(x, item, line)
          }
        case .point:
          switch item.x {
          case .label(let x): pointMark(x, item, point)
          case .number(let x): pointMark(x, item, point)
          }
        case .bar:
          switch item.x {
          case .label(let x): barMark(x, item, bar, individualColors)
          case .number(let x): barMark(x, item, bar, individualColors)
          }
        case .area:
          switch item.x {
          case .label(let x): AreaMark(x: .value("X", x), y: .value("Y", item.y)).foregroundStyle(area.color.map(oneNativeChartColor) ?? .blue)
          case .number(let x): AreaMark(x: .value("X", x), y: .value("Y", item.y)).foregroundStyle(area.color.map(oneNativeChartColor) ?? .blue)
          }
        case .pie:
          let sector = SectorMark(angle: .value("Value", item.y), innerRadius: .ratio(pie.innerRadius ?? 0.3), angularInset: pie.angularInset ?? 2)
          if individualColors {
            sector.foregroundStyle(item.color ?? .blue).opacity(0.8)
          } else {
            sector.foregroundStyle(by: .value("Category", item.label)).opacity(0.8)
          }
        case .rectangle:
          switch item.x {
          case .label(let x): rectangleMark(x, item, rectangle)
          case .number(let x): rectangleMark(x, item, rectangle)
          }
        }
      }
      ForEach(Array(rules.enumerated()), id: \.offset) { _, item in
        RuleMark(y: .value("Reference", item.y))
          .foregroundStyle(item.color ?? rule.color.map(oneNativeChartColor) ?? .red)
          .lineStyle(StrokeStyle(lineWidth: rule.lineWidth ?? 2, dash: (rule.dashArray ?? []).map { CGFloat($0) }))
      }
    }
    .chartXAxis(model.showGrid ? .visible : .hidden)
    .chartYAxis(model.showGrid ? .visible : .hidden)
    .animation(model.animate ? .easeInOut : nil, value: points.count)
    .chartLegend(position: .automatic, spacing: 16)
    .chartLegend(model.showLegend ? .visible : .hidden)
  }

  private func lineMark<X: Plottable>(_ x: X, _ item: OneNativeChartPoint, _ style: OneNativeChartStyle) -> some Charts.ChartContent {
    LineMark(x: .value("X", x), y: .value("Y", item.y))
      .foregroundStyle(style.color.map(oneNativeChartColor) ?? .blue)
      .symbol(oneNativeChartSymbol(style.pointStyle))
      .symbolSize(CGFloat(style.pointSize ?? 6))
      .lineStyle(StrokeStyle(lineWidth: style.width ?? 2, dash: (style.dashArray ?? []).map { CGFloat($0) }))
  }

  private func pointMark<X: Plottable>(_ x: X, _ item: OneNativeChartPoint, _ style: OneNativeChartStyle) -> some Charts.ChartContent {
    PointMark(x: .value("X", x), y: .value("Y", item.y))
      .foregroundStyle(item.color ?? .blue)
      .symbol(oneNativeChartSymbol(style.pointStyle))
      .symbolSize(CGFloat(style.pointSize ?? 6))
  }

  @ChartContentBuilder
  private func barMark<X: Plottable>(_ x: X, _ item: OneNativeChartPoint, _ style: OneNativeChartStyle, _ individualColors: Bool) -> some Charts.ChartContent {
    let mark = style.width.map { BarMark(x: .value("X", x), y: .value("Y", item.y), width: .fixed(CGFloat($0))) }
      ?? BarMark(x: .value("X", x), y: .value("Y", item.y))
    if individualColors {
      mark.foregroundStyle(item.color ?? .blue).cornerRadius(CGFloat(style.cornerRadius ?? 0))
    } else {
      mark.foregroundStyle(by: .value("Category", item.label)).cornerRadius(CGFloat(style.cornerRadius ?? 0))
    }
  }

  private func rectangleMark<X: Plottable>(_ x: X, _ item: OneNativeChartPoint, _ style: OneNativeChartStyle) -> some Charts.ChartContent {
    RectangleMark(x: .value("X", x), y: .value("Y", item.y))
      .foregroundStyle(item.color ?? style.color.map(oneNativeChartColor) ?? .blue)
      .cornerRadius(CGFloat(style.cornerRadius ?? 0))
  }
}
