import SwiftUI
import Charts

struct EvolutionChartCard: View {
    let data: [(date: Date, value: Decimal)]
    let categoryData: [CategoryEvolutionPoint]
    let goals: [Goal]

    @State private var selectedDataPoint: (date: Date, value: Decimal)?
    @State private var chartMode: ChartMode = .total
    @State private var showGoalLines = true
    @State private var chartWidth: CGFloat = 300

    enum ChartMode: String, CaseIterable, Identifiable {
        case total = "Total"
        case byCategory = "By Category"

        var id: String { rawValue }
    }

    private static let compactXAxisDateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.locale = .autoupdatingCurrent
        formatter.setLocalizedDateFormatFromTemplate("MMM yy")
        return formatter
    }()

    /// Calculates the optimal month stride so labels never overlap,
    /// using the actual rendered width of the chart instead of just data count.
    private func xAxisMonthStride(for width: CGFloat) -> Int {
        // ~50pt for Y-axis, ~44pt per "Jan 24" label
        let usableWidth = max(width - 50, 80)
        let maxLabels = max(2, Int(usableWidth / 44))
        let rawStride = max(1, Int(ceil(Double(data.count) / Double(maxLabels))))
        switch rawStride {
        case ...1: return 1
        case ...2: return 2
        case ...3: return 3
        case ...4: return 4
        case ...6: return 6
        default:   return 12
        }
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            headerView
            modePicker
            chartSection
        }
        .padding()
        .background(Color(.systemBackground))
        .cornerRadius(AppConstants.UI.cornerRadius)
        .shadow(color: .black.opacity(0.05), radius: 8, y: 2)
    }

    private var headerView: some View {
        HStack {
            Text("Portfolio Evolution")
                .font(.headline)

            Spacer()

            Button {
                showGoalLines.toggle()
            } label: {
                Image(systemName: showGoalLines ? "target" : "slash.circle")
                    .foregroundColor(.secondary)
            }
            .accessibilityLabel(showGoalLines ? "Hide goals" : "Show goals")

            if let selected = selectedDataPoint, chartMode == .total {
                VStack(alignment: .trailing) {
                    Text(selected.value.compactCurrencyString)
                        .font(.subheadline.weight(.semibold))
                    Text(selected.date.monthYearString)
                        .font(.caption)
                        .foregroundColor(.secondary)
                }
            }
        }
    }

    private var modePicker: some View {
        Picker("Evolution Mode", selection: $chartMode) {
            ForEach(ChartMode.allCases) { mode in
                Text(mode.rawValue).tag(mode)
            }
        }
        .pickerStyle(.segmented)
    }

    @ViewBuilder
    private var chartSection: some View {
        if data.count >= 2 {
            chartView
        } else {
            Text("Not enough data to display chart")
                .font(.subheadline)
                .foregroundColor(.secondary)
                .frame(height: 200)
                .frame(maxWidth: .infinity)
        }
    }

    private var chartView: some View {
        Chart {
            chartMarks
        }
        .chartForegroundStyleScale(domain: chartCategoryNames, range: chartCategoryColors)
        .chartXAxis {
            AxisMarks(values: .stride(by: .month, count: xAxisMonthStride(for: chartWidth))) { value in
                AxisGridLine(stroke: StrokeStyle(lineWidth: 0.8, dash: [3, 3]))
                    .foregroundStyle(Color.secondary.opacity(0.2))
                AxisTick(stroke: StrokeStyle(lineWidth: 0.8))
                    .foregroundStyle(Color.secondary.opacity(0.28))
                AxisValueLabel {
                    if let date = value.as(Date.self) {
                        Text(date, formatter: Self.compactXAxisDateFormatter)
                            .font(.caption2)
                    }
                }
            }
        }
        .chartYAxis {
            AxisMarks(position: .leading) { value in
                AxisValueLabel {
                    if let doubleValue = value.as(Double.self) {
                        Text(Decimal(doubleValue).shortCurrencyString)
                            .font(.caption)
                    }
                }
            }
        }
        .chartOverlay { proxy in
            GeometryReader { geometry in
                Rectangle()
                    .fill(.clear)
                    .contentShape(Rectangle())
                    .gesture(
                        DragGesture(minimumDistance: 0)
                            .onChanged { value in
                                guard let plotFrameAnchor = proxy.plotFrame else { return }
                                let plotFrame = geometry[plotFrameAnchor]
                                let x = value.location.x - plotFrame.origin.x
                                guard let date: Date = proxy.value(atX: x) else { return }

                                if let closest = data.min(by: {
                                    abs($0.date.timeIntervalSince(date)) < abs($1.date.timeIntervalSince(date))
                                }) {
                                    selectedDataPoint = closest
                                }
                            }
                            .onEnded { _ in
                                selectedDataPoint = nil
                            }
                    )
            }
        }
        .frame(height: 200)
        .background(
            GeometryReader { geo in
                Color.clear
                    .onAppear { chartWidth = geo.size.width }
                    .onChange(of: geo.size.width) { _, w in chartWidth = w }
            }
        )
        // Performance: Use GPU rendering for smoother scrolling
        .drawingGroup()
    }

    @ChartContentBuilder
    private var chartMarks: some ChartContent {
        switch chartMode {
        case .total:
            ForEach(data, id: \.date) { item in
                LineMark(
                    x: .value("Date", item.date),
                    y: .value("Value", NSDecimalNumber(decimal: item.value).doubleValue)
                )
                .foregroundStyle(Color.appPrimary)
                .interpolationMethod(.catmullRom)

                PointMark(
                    x: .value("Date", item.date),
                    y: .value("Value", NSDecimalNumber(decimal: item.value).doubleValue)
                )
                .foregroundStyle(Color.appPrimary)
                .symbolSize(26)

                AreaMark(
                    x: .value("Date", item.date),
                    y: .value("Value", NSDecimalNumber(decimal: item.value).doubleValue)
                )
                .foregroundStyle(
                    LinearGradient(
                        colors: [Color.appPrimary.opacity(0.3), Color.appPrimary.opacity(0.0)],
                        startPoint: .top,
                        endPoint: .bottom
                    )
                )
                .interpolationMethod(.catmullRom)
            }
        case .byCategory:
            ForEach(stackedCategoryData) { item in
                AreaMark(
                    x: .value("Date", item.date),
                    yStart: .value("Start", NSDecimalNumber(decimal: item.start).doubleValue),
                    yEnd: .value("End", NSDecimalNumber(decimal: item.end).doubleValue)
                )
                .foregroundStyle(by: .value("Category", item.categoryName))
                .interpolationMethod(.catmullRom)
            }
        }

        if showGoalLines {
            ForEach(goals) { goal in
                RuleMark(y: .value("Goal", NSDecimalNumber(decimal: goal.targetDecimal).doubleValue))
                    .foregroundStyle(Color.appSecondary.opacity(0.5))
                    .lineStyle(StrokeStyle(lineWidth: 1, dash: [6, 4]))
                    .annotation(position: .topTrailing) {
                        Text(goal.name)
                            .font(.caption2)
                            .foregroundColor(.secondary)
                    }
            }
        }

        if let selected = selectedDataPoint, chartMode == .total {
            RuleMark(x: .value("Selected", selected.date))
                .foregroundStyle(Color.gray.opacity(0.3))
                .lineStyle(StrokeStyle(lineWidth: 1, dash: [5, 5]))

            PointMark(
                x: .value("Date", selected.date),
                y: .value("Value", NSDecimalNumber(decimal: selected.value).doubleValue)
            )
            .foregroundStyle(Color.appPrimary)
            .symbolSize(100)
        }
    }

    private var chartCategoryNames: [String] {
        let names = Array(Set(categoryData.map { $0.categoryName })).sorted()
        return names
    }

    private struct StackedCategoryPoint: Identifiable {
        let date: Date
        let categoryName: String
        let colorHex: String
        let start: Decimal
        let end: Decimal

        var id: String {
            "\(categoryName)-\(date.timeIntervalSince1970)"
        }
    }

    private var stackedCategoryData: [StackedCategoryPoint] {
        let grouped = Dictionary(grouping: categoryData) { $0.date }
        let dates = grouped.keys.sorted()
        let categories = chartCategoryNames
        var stacked: [StackedCategoryPoint] = []

        for date in dates {
            let points = grouped[date] ?? []
            var running: Decimal = 0

            for category in categories {
                let value = points.first(where: { $0.categoryName == category })?.value ?? 0
                let start = running
                let end = running + value
                running = end

                if let colorHex = points.first(where: { $0.categoryName == category })?.colorHex {
                    stacked.append(StackedCategoryPoint(
                        date: date,
                        categoryName: category,
                        colorHex: colorHex,
                        start: start,
                        end: end
                    ))
                }
            }
        }

        return stacked
    }

    private var chartCategoryColors: [Color] {
        chartCategoryNames.map { name in
            if let hex = categoryData.first(where: { $0.categoryName == name })?.colorHex {
                return Color(hex: hex) ?? .gray
            }
            return .gray
        }
    }
}

// MARK: - Mini Sparkline

struct SparklineView: View {
    let data: [(date: Date, value: Decimal)]
    let color: Color

    var body: some View {
        if data.count >= 2 {
            Chart(data, id: \.date) { item in
                LineMark(
                    x: .value("Date", item.date),
                    y: .value("Value", NSDecimalNumber(decimal: item.value).doubleValue)
                )
                .foregroundStyle(color)
                .interpolationMethod(.catmullRom)
            }
            .chartXAxis(.hidden)
            .chartYAxis(.hidden)
            .chartLegend(.hidden)
        } else {
            Rectangle()
                .fill(Color.gray.opacity(0.1))
        }
    }
}

#Preview {
    let sampleData: [(date: Date, value: Decimal)] = [
        (Date().adding(months: -6), 10000),
        (Date().adding(months: -5), 10500),
        (Date().adding(months: -4), 10200),
        (Date().adding(months: -3), 11000),
        (Date().adding(months: -2), 11500),
        (Date().adding(months: -1), 11200),
        (Date(), 12000)
    ]

    return EvolutionChartCard(data: sampleData, categoryData: [], goals: [])
        .padding()
}
