import WidgetKit
import SwiftUI
import CoreData

private let appGroupIdentifier = "group.com.alexandrevazquez.portfoliojournal"
private let storeFileName = "PortfolioJournal.sqlite"
private let sharedPremiumKey = "premiumUnlocked"
private let widgetPrimaryColor = Color(hex: "#3B82F6") ?? .blue
private let widgetSecondaryColor = Color(hex: "#10B981") ?? .green

private func sharedStoreURL() -> URL? {
    return FileManager.default
        .containerURL(forSecurityApplicationGroupIdentifier: appGroupIdentifier)?
        .appendingPathComponent(storeFileName)
}

private func createWidgetContainer() -> NSPersistentContainer? {
    guard let modelURL = Bundle.main.url(forResource: "PortfolioJournal", withExtension: "momd"),
          let model = NSManagedObjectModel(contentsOf: modelURL) else {
        return nil
    }

    let container = NSPersistentContainer(name: "PortfolioJournal", managedObjectModel: model)

    guard let storeURL = sharedStoreURL(),
          FileManager.default.fileExists(atPath: storeURL.path) else {
        return nil
    }

    let description = NSPersistentStoreDescription(url: storeURL)
    description.isReadOnly = true
    description.shouldMigrateStoreAutomatically = true
    description.shouldInferMappingModelAutomatically = true

    container.persistentStoreDescriptions = [description]

    var loadError: Error?
    container.loadPersistentStores { _, error in
        loadError = error
    }

    if loadError != nil {
        return nil
    }

    return container
}

private func fetchCurrencyCode(from container: NSPersistentContainer?) -> String {
    guard let container = container else { return "EUR" }
    let context = container.viewContext
    let request = NSFetchRequest<NSManagedObject>(entityName: "AppSettings")
    request.fetchLimit = 1
    if let settings = try? context.fetch(request).first,
       let code = settings.value(forKey: "currency") as? String,
       !code.isEmpty {
        return code
    }
    return "EUR"
}

// MARK: - Widget Entry

struct InvestmentWidgetEntry: TimelineEntry {
    let date: Date
    let isPremium: Bool
    let totalValue: Decimal
    let dayChange: Decimal
    let dayChangePercentage: Double
    let topSources: [(name: String, value: Decimal, color: String)]
    let trendPoints: [Decimal]
    let trendLabels: [String]
    let categoryEvolution: [CategorySeries]
    let categoryTotals: [(name: String, value: Decimal, color: String)]
    let goals: [GoalSummary]
    let currencyCode: String
    let insightTitle: String
    let insightValue: String
}

struct CategorySeries: Identifiable {
    let id: String
    let name: String
    let color: String
    let points: [Decimal]
    let latestValue: Decimal
}

struct GoalSummary: Identifiable {
    let id = UUID()
    let name: String
    let targetAmount: Decimal
    let targetDate: Date?
}

// MARK: - Widget Provider

struct InvestmentWidgetProvider: TimelineProvider {
    func placeholder(in context: Context) -> InvestmentWidgetEntry {
        InvestmentWidgetEntry(
            date: Date(),
            isPremium: true,
            totalValue: 50000,
            dayChange: 250,
            dayChangePercentage: 0.5,
            topSources: [
                ("Stocks", 30000, "#10B981"),
                ("Bonds", 15000, "#3B82F6"),
                ("Real Estate", 5000, "#F59E0B")
            ],
            trendPoints: [45000, 46000, 47000, 48000, 49000, 50000],
            trendLabels: ["Aug", "Sep", "Oct", "Nov", "Dec", "Jan"],
            categoryEvolution: [
                CategorySeries(
                    id: "stocks",
                    name: "Stocks",
                    color: "#10B981",
                    points: [20000, 21000, 22000, 23000, 24000, 25000, 26000],
                    latestValue: 26000
                ),
                CategorySeries(
                    id: "bonds",
                    name: "Bonds",
                    color: "#3B82F6",
                    points: [12000, 12000, 12500, 13000, 13500, 14000, 14500],
                    latestValue: 14500
                ),
                CategorySeries(
                    id: "realestate",
                    name: "Real Estate",
                    color: "#F59E0B",
                    points: [6000, 6200, 6400, 6500, 6600, 6700, 6800],
                    latestValue: 6800
                )
            ],
            categoryTotals: [
                ("Stocks", 26000, "#10B981"),
                ("Bonds", 14500, "#3B82F6"),
                ("Real Estate", 6800, "#F59E0B")
            ],
            goals: [
                GoalSummary(name: "Target", targetAmount: 75000, targetDate: nil)
            ],
            currencyCode: "EUR",
            insightTitle: "Milestone ahead",
            insightValue: "€5K from €50K"
        )
    }

    func getSnapshot(in context: Context, completion: @escaping (InvestmentWidgetEntry) -> Void) {
        let entry = fetchData()
        completion(entry)
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<InvestmentWidgetEntry>) -> Void) {
        let entry = fetchData()

        // Refresh every hour
        let nextUpdate = Calendar.current.date(byAdding: .hour, value: 1, to: Date()) ?? Date()
        let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))

        completion(timeline)
    }

    private func fetchData() -> InvestmentWidgetEntry {
        let isPremium = UserDefaults(suiteName: appGroupIdentifier)?.bool(forKey: sharedPremiumKey) ?? false
        let container = createWidgetContainer()
        let currencyCode = fetchCurrencyCode(from: container)

        guard let container = container else {
            return InvestmentWidgetEntry(
                date: Date(),
                isPremium: isPremium,
                totalValue: 0,
                dayChange: 0,
                dayChangePercentage: 0,
                topSources: [],
                trendPoints: [],
                trendLabels: [],
                categoryEvolution: [],
                categoryTotals: [],
                goals: [],
                currencyCode: currencyCode,
                insightTitle: "",
                insightValue: ""
            )
        }

        let context = container.viewContext

        func decimalValue(from object: NSManagedObject, key: String) -> Decimal {
            if let number = object.value(forKey: key) as? NSDecimalNumber {
                return number.decimalValue
            }
            if let dbl = object.value(forKey: key) as? Double {
                return Decimal(dbl)
            }
            if let num = object.value(forKey: key) as? NSNumber {
                return Decimal(num.doubleValue)
            }
            return .zero
        }

        // Build daily totals from snapshots for change/sparkline and category evolution.
        let snapshotRequest = NSFetchRequest<NSManagedObject>(entityName: "Snapshot")
        snapshotRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)]
        let snapshots = (try? context.fetch(snapshotRequest)) ?? []

        var dailyTotals: [Date: Decimal] = [:]
        var monthlyTotals: [Date: Decimal] = [:]
        var latestBySource: [NSManagedObjectID: (date: Date, value: Decimal, source: NSManagedObject)] = [:]
        var categoryMonthlyTotals: [String: [Date: Decimal]] = [:]
        var categoryMeta: [String: (name: String, color: String)] = [:]
        let calendar = Calendar.current
        for snapshot in snapshots {
            guard let rawDate = snapshot.value(forKey: "date") as? Date else { continue }
            let day = calendar.startOfDay(for: rawDate)
            let month = calendar.date(from: calendar.dateComponents([.year, .month], from: rawDate)) ?? day
            let value = decimalValue(from: snapshot, key: "value")
            dailyTotals[day, default: .zero] += value
            monthlyTotals[month, default: .zero] += value

            if let source = snapshot.value(forKey: "source") as? NSManagedObject {
                let sourceId = source.objectID
                if let existing = latestBySource[sourceId] {
                    if rawDate > existing.date {
                        latestBySource[sourceId] = (rawDate, value, source)
                    }
                } else {
                    latestBySource[sourceId] = (rawDate, value, source)
                }

                var categoryId = "uncategorized"
                var categoryName = "Uncategorized"
                var categoryColor = "#94A3B8"
                if let category = source.value(forKey: "category") as? NSManagedObject {
                    categoryId = category.objectID.uriRepresentation().absoluteString
                    categoryName = (category.value(forKey: "name") as? String) ?? categoryName
                    if let colorHex = category.value(forKey: "colorHex") as? String, !colorHex.isEmpty {
                        categoryColor = colorHex
                    }
                }

                categoryMeta[categoryId] = (categoryName, categoryColor)
                var monthTotals = categoryMonthlyTotals[categoryId, default: [:]]
                monthTotals[month, default: .zero] += value
                categoryMonthlyTotals[categoryId] = monthTotals
            }
        }

        let sortedTotals = dailyTotals
            .map { ($0.key, $0.value) }
            .sorted { $0.0 < $1.0 }

        let sortedMonths = monthlyTotals
            .map { ($0.key, $0.value) }
            .sorted { $0.0 < $1.0 }
        let months: [Date]
        let trendPoints: [Decimal]
        let trendLabels: [String]
        if sortedMonths.isEmpty {
            months = []
            trendPoints = []
            trendLabels = []
        } else {
            let latestMonth = sortedMonths.last?.0 ??
                (calendar.date(from: calendar.dateComponents([.year, .month], from: Date())) ?? Date())
            months = (0..<6).reversed().compactMap { offset in
                calendar.date(byAdding: .month, value: -offset, to: latestMonth)
            }
            let monthFormatter = DateFormatter()
            monthFormatter.dateFormat = "MMM"
            trendPoints = months.map { month in
                if let value = monthlyTotals[month] { return value }
                // Forward-fill: use the most recent earlier month's value
                let previous = sortedMonths.last { $0.0 < month }
                return previous?.1 ?? .zero
            }
            trendLabels = months.map { monthFormatter.string(from: $0) }
        }

        let totalValue = latestBySource.values.reduce(Decimal.zero) { $0 + $1.value }

        let mappedSources: [(name: String, value: Decimal, color: String)] = latestBySource.values.map { entry in
            let source = entry.source
            let name = (source.value(forKey: "name") as? String) ?? "Unknown"
            var color = "#3B82F6"
            if let category = source.value(forKey: "category") as? NSManagedObject,
               let colorHex = category.value(forKey: "colorHex") as? String,
               !colorHex.isEmpty {
                color = colorHex
            }
            return (name: name, value: entry.value, color: color)
        }

        let topSources = mappedSources
            .sorted { $0.value > $1.value }
            .prefix(3)

        var categoryTotalsMap: [String: Decimal] = [:]
        for entry in latestBySource.values {
            let source = entry.source
            let categoryId: String = {
                if let category = source.value(forKey: "category") as? NSManagedObject {
                    return category.objectID.uriRepresentation().absoluteString
                }
                return "uncategorized"
            }()
            categoryTotalsMap[categoryId, default: .zero] += entry.value
        }

        let categoryTotalsData = categoryTotalsMap
            .map { key, value in
                let meta = categoryMeta[key] ?? ("Uncategorized", "#94A3B8")
                return (id: key, name: meta.0, value: value, color: meta.1)
            }
            .sorted { $0.value > $1.value }

        let categoryEvolution: [CategorySeries] = categoryTotalsData.prefix(4).map { category in
            let monthMap = categoryMonthlyTotals[category.id] ?? [:]
            let sortedCategoryMonths = monthMap.map { ($0.key, $0.value) }.sorted { $0.0 < $1.0 }
            let points = months.map { month -> Decimal in
                if let value = monthMap[month] { return value }
                // Forward-fill: use the most recent earlier month's value
                let previous = sortedCategoryMonths.last { $0.0 < month }
                return previous?.1 ?? .zero
            }
            return CategorySeries(
                id: category.id,
                name: category.name,
                color: category.color,
                points: points,
                latestValue: category.value
            )
        }

        var dayChange: Decimal = 0
        var dayChangePercentage: Double = 0
        if sortedTotals.count >= 2 {
            let last = sortedTotals[sortedTotals.count - 1].1
            let previous = sortedTotals[sortedTotals.count - 2].1
            dayChange = last - previous
            if previous != 0 {
                dayChangePercentage = NSDecimalNumber(decimal: dayChange / previous).doubleValue * 100
            }
        }

        let goalsRequest = NSFetchRequest<NSManagedObject>(entityName: "Goal")
        goalsRequest.predicate = NSPredicate(format: "isActive == YES")
        let goalObjects = (try? context.fetch(goalsRequest)) ?? []
        let goalSummaries = goalObjects.compactMap { goal -> GoalSummary? in
            let amount = decimalValue(from: goal, key: "targetAmount")
            guard amount > 0 else { return nil }
            let name = (goal.value(forKey: "name") as? String)?.trimmingCharacters(in: .whitespacesAndNewlines)
            let targetDate = goal.value(forKey: "targetDate") as? Date
            return GoalSummary(name: (name?.isEmpty == false ? name! : "Goal"), targetAmount: amount, targetDate: targetDate)
        }
        let goals = goalSummaries.sorted { lhs, rhs in
            switch (lhs.targetDate, rhs.targetDate) {
            case let (lDate?, rDate?):
                return lDate < rDate
            case (_?, nil):
                return true
            case (nil, _?):
                return false
            default:
                return lhs.targetAmount < rhs.targetAmount
            }
        }

        // Compute top insight
        var insightTitle = ""
        var insightValue = ""

        // Milestone approaching
        let milestones: [Double] = [1000, 2500, 5000, 10000, 25000, 50000, 100000, 250000, 500000, 1_000_000]
        let totalDouble = NSDecimalNumber(decimal: totalValue).doubleValue
        if let nextMs = milestones.first(where: { $0 > totalDouble }) {
            let pct = totalDouble / nextMs
            if pct >= 0.90 {
                let gap = nextMs - totalDouble
                let formatter = NumberFormatter()
                formatter.numberStyle = .currency
                formatter.currencyCode = currencyCode
                formatter.maximumFractionDigits = 0
                let msStr = formatter.string(from: NSNumber(value: nextMs)) ?? ""
                let gapStr = formatter.string(from: NSNumber(value: gap)) ?? ""
                insightTitle = "Milestone ahead"
                insightValue = "\(gapStr) from \(msStr)"
            }
        }

        // Year performance (only if no milestone insight)
        if insightTitle.isEmpty && !trendPoints.isEmpty && trendPoints.count >= 2 {
            let first = NSDecimalNumber(decimal: trendPoints.first!).doubleValue
            let last = NSDecimalNumber(decimal: trendPoints.last!).doubleValue
            if first > 0 {
                let pct = (last - first) / first * 100
                if abs(pct) >= 1 {
                    insightTitle = "Year to date"
                    insightValue = String(format: "%+.1f%%", pct)
                }
            }
        }

        return InvestmentWidgetEntry(
            date: Date(),
            isPremium: isPremium,
            totalValue: totalValue,
            dayChange: dayChange,
            dayChangePercentage: dayChangePercentage,
            topSources: Array(topSources),
            trendPoints: trendPoints,
            trendLabels: trendLabels,
            categoryEvolution: categoryEvolution,
            categoryTotals: categoryTotalsData.map { (name: $0.name, value: $0.value, color: $0.color) },
            goals: goals,
            currencyCode: currencyCode,
            insightTitle: insightTitle,
            insightValue: insightValue
        )
    }
}

// MARK: - Small Widget View

struct SmallWidgetView: View {
    let entry: InvestmentWidgetEntry

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            Text("Total Value")
                .font(.caption)
                .foregroundColor(.secondary)

            Text(entry.totalValue.compactCurrencyString(currencyCode: entry.currencyCode))
                .font(.title2.weight(.bold))
                .minimumScaleFactor(0.7)
                .lineLimit(1)

            VStack(alignment: .leading, spacing: 4) {
                Image(systemName: entry.dayChange >= 0 ? "arrow.up.right" : "arrow.down.right")
                    .font(.caption2)

                Text(entry.dayChange.compactCurrencyString(currencyCode: entry.currencyCode))
                    .font(.caption.weight(.medium))

                Text(String(format: "%.1f%% since last", entry.dayChangePercentage))
                    .font(.caption2)
                    .foregroundColor(.secondary)
            }
            .foregroundColor(entry.dayChange >= 0 ? .green : .red)

            Spacer()
        }
        .padding()
        .containerBackground(.background, for: .widget)
    }
}

// MARK: - Medium Widget View

struct MediumWidgetView: View {
    let entry: InvestmentWidgetEntry

    var body: some View {
        HStack(spacing: 16) {
            // Left side - Total value
            VStack(alignment: .leading, spacing: 8) {
                Text("Portfolio")
                    .font(.caption)
                    .foregroundColor(.secondary)

                Text(entry.totalValue.compactCurrencyString(currencyCode: entry.currencyCode))
                    .font(.title.weight(.bold))
                    .minimumScaleFactor(0.7)
                    .lineLimit(1)

                HStack(spacing: 4) {
                    Image(systemName: entry.dayChange >= 0 ? "arrow.up.right" : "arrow.down.right")
                        .font(.caption2)

                    Text(entry.dayChange.compactCurrencyString(currencyCode: entry.currencyCode))
                        .font(.caption.weight(.medium))

                    Text("(\(String(format: "%.1f%%", entry.dayChangePercentage)))")
                        .font(.caption)
                        .foregroundColor(.secondary)
                }
                .foregroundColor(entry.dayChange >= 0 ? .green : .red)
            }

            Spacer()

            VStack(alignment: .trailing, spacing: 8) {
                if entry.isPremium {
                    if entry.trendPoints.count >= 2 {
                        TrendLineChartView(
                            points: entry.trendPoints,
                            labels: entry.trendLabels,
                            goal: entry.goals.first,
                            currencyCode: entry.currencyCode
                        )
                        .frame(height: 70)
                    } else {
                        VStack(alignment: .trailing, spacing: 4) {
                            Text("Add snapshots")
                                .font(.caption2)
                                .foregroundColor(.secondary)
                            Text("to see trend")
                                .font(.caption.weight(.semibold))
                                .foregroundColor(.secondary)
                        }
                    }
                } else {
                    VStack(alignment: .trailing, spacing: 4) {
                        Text("Sparkline")
                            .font(.caption2)
                            .foregroundColor(.secondary)
                        Text("Premium")
                            .font(.caption.weight(.semibold))
                            .foregroundColor(.secondary)
                    }
                }

                // Top sources
                VStack(alignment: .trailing, spacing: 6) {
                    ForEach(entry.topSources, id: \.name) { source in
                        HStack(spacing: 6) {
                            Circle()
                                .fill(Color(hex: source.color) ?? .gray)
                                .frame(width: 8, height: 8)

                            Text(source.name)
                                .font(.caption)
                                .foregroundColor(.secondary)
                                .lineLimit(1)

                            Text(source.value.shortCurrencyString(currencyCode: entry.currencyCode))
                                .font(.caption.weight(.medium))
                        }
                    }
                }

                if !entry.insightTitle.isEmpty {
                    HStack(spacing: 4) {
                        Image(systemName: "lightbulb.fill")
                            .font(.caption2)
                            .foregroundColor(.orange)
                        VStack(alignment: .leading, spacing: 0) {
                            Text(entry.insightTitle)
                                .font(.caption2)
                                .foregroundColor(.secondary)
                            Text(entry.insightValue)
                                .font(.caption.weight(.semibold))
                                .foregroundColor(.primary)
                                .lineLimit(1)
                        }
                    }
                }
            }
        }
        .padding()
        .containerBackground(.background, for: .widget)
    }
}

// MARK: - Large Widget View

struct LargeWidgetView: View {
    let entry: InvestmentWidgetEntry
    private var hasCategoryTrend: Bool {
        (entry.categoryEvolution.first?.points.count ?? 0) >= 2
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            HStack(alignment: .top) {
                VStack(alignment: .leading, spacing: 6) {
                    Text("Portfolio")
                        .font(.caption)
                        .foregroundColor(.secondary)

                    Text(entry.totalValue.compactCurrencyString(currencyCode: entry.currencyCode))
                        .font(.title2.weight(.bold))
                        .minimumScaleFactor(0.7)
                        .lineLimit(1)

                    HStack(spacing: 4) {
                        Image(systemName: entry.dayChange >= 0 ? "arrow.up.right" : "arrow.down.right")
                            .font(.caption2)

                        Text(entry.dayChange.compactCurrencyString(currencyCode: entry.currencyCode))
                            .font(.caption.weight(.medium))

                        Text("(\(String(format: "%.1f%%", entry.dayChangePercentage)))")
                            .font(.caption2)
                            .foregroundColor(.secondary)
                    }
                    .foregroundColor(entry.dayChange >= 0 ? .green : .red)
                }

                Spacer()

                Text("Category Evolution")
                    .font(.caption.weight(.semibold))
                    .foregroundColor(.secondary)
            }

            if entry.isPremium {
                if hasCategoryTrend {
                    CombinedCategoryChartView(
                        series: entry.categoryEvolution,
                        labels: entry.trendLabels,
                        goal: entry.goals.first,
                        currencyCode: entry.currencyCode
                    )
                    .frame(height: 98)

                    VStack(alignment: .leading, spacing: 8) {
                        ForEach(entry.categoryTotals.prefix(4), id: \.name) { category in
                            HStack {
                                RoundedRectangle(cornerRadius: 3)
                                    .fill(Color(hex: category.color) ?? .gray)
                                    .frame(width: 10, height: 10)

                                Text(category.name)
                                    .font(.caption)
                                    .foregroundColor(.secondary)

                                Spacer()

                                Text(category.value.shortCurrencyString(currencyCode: entry.currencyCode))
                                    .font(.caption.weight(.medium))
                            }
                        }
                    }
                } else {
                    VStack(alignment: .leading, spacing: 6) {
                        Text("Add snapshots")
                            .font(.caption.weight(.semibold))
                        Text("Category evolution appears after updates.")
                            .font(.caption2)
                            .foregroundColor(.secondary)
                    }
                    .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
                }
            } else {
                VStack(alignment: .leading, spacing: 6) {
                    Text("Unlock category trends")
                        .font(.caption.weight(.semibold))
                    Text("Premium shows evolution by category.")
                        .font(.caption2)
                        .foregroundColor(.secondary)
                }
                .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
            }
        }
        .padding()
        .containerBackground(.background, for: .widget)
    }
}

// MARK: - Accessory Circular View (Lock Screen)

struct AccessoryCircularView: View {
    let entry: InvestmentWidgetEntry

    var body: some View {
        ZStack {
            AccessoryWidgetBackground()

            VStack(spacing: 2) {
                Image(systemName: entry.dayChange >= 0 ? "arrow.up.right" : "arrow.down.right")
                    .font(.caption)

                Text(String(format: "%.1f%%", entry.dayChangePercentage))
                    .font(.caption2.weight(.semibold))
            }
        }
        .containerBackground(.background, for: .widget)
    }
}

// MARK: - Accessory Rectangular View (Lock Screen)

struct AccessoryRectangularView: View {
    let entry: InvestmentWidgetEntry

    var body: some View {
        VStack(alignment: .leading, spacing: 2) {
            Text("Portfolio")
                .font(.caption2)
                .foregroundColor(.secondary)

            Text(entry.totalValue.compactCurrencyString(currencyCode: entry.currencyCode))
                .font(.headline)

            HStack(spacing: 4) {
                Image(systemName: entry.dayChange >= 0 ? "arrow.up.right" : "arrow.down.right")
                    .font(.caption2)

                Text(String(format: "%.1f%%", entry.dayChangePercentage))
                    .font(.caption2)
            }
            .foregroundColor(entry.dayChange >= 0 ? .green : .red)
        }
        .containerBackground(.background, for: .widget)
    }
}

// MARK: - Trend Line Chart

struct TrendLineChartView: View {
    let points: [Decimal]
    let labels: [String]
    let goal: GoalSummary?
    var currencyCode: String = "EUR"

    private var values: [Double] {
        points.map { NSDecimalNumber(decimal: $0).doubleValue }
    }

    private var minValue: Double {
        values.min() ?? 0
    }

    private var maxValue: Double {
        values.max() ?? 0
    }

    private func normalized(_ value: Double) -> CGFloat {
        let minV = minValue
        let maxV = maxValue
        if minV == maxV { return 0.5 }
        return CGFloat((value - minV) / (maxV - minV))
    }

    var body: some View {
        HStack(alignment: .center, spacing: 6) {
            VStack(alignment: .leading, spacing: 2) {
                Text(Decimal(maxValue).shortCurrencyString(currencyCode: currencyCode))
                    .font(.caption2)
                    .foregroundColor(.secondary)
                Spacer()
                Text(Decimal(minValue).shortCurrencyString(currencyCode: currencyCode))
                    .font(.caption2)
                    .foregroundColor(.secondary)
            }

            VStack(spacing: 4) {
                GeometryReader { geo in
                    let width = geo.size.width
                    let height = geo.size.height
                    let step = points.count > 1 ? width / CGFloat(points.count - 1) : width

                    Path { path in
                        guard !values.isEmpty else { return }
                        for (index, value) in values.enumerated() {
                            let x = CGFloat(index) * step
                            let y = height - (normalized(value) * height)
                            if index == 0 {
                                path.move(to: CGPoint(x: x, y: y))
                            } else {
                                path.addLine(to: CGPoint(x: x, y: y))
                            }
                        }
                    }
                    .stroke(
                        LinearGradient(
                            colors: [
                                widgetPrimaryColor.opacity(0.9),
                                widgetPrimaryColor.opacity(0.6)
                            ],
                            startPoint: .leading,
                            endPoint: .trailing
                        ),
                        style: StrokeStyle(lineWidth: 2, lineJoin: .round)
                    )

                    if let goal, maxValue > 0 {
                        let goalValue = NSDecimalNumber(decimal: goal.targetAmount).doubleValue
                        let goalY = height - (normalized(goalValue) * height)
                        Path { path in
                            path.move(to: CGPoint(x: 0, y: goalY))
                            path.addLine(to: CGPoint(x: width, y: goalY))
                        }
                        .stroke(widgetSecondaryColor.opacity(0.6), style: StrokeStyle(lineWidth: 1, dash: [4, 3]))
                    }
                }

                HStack(spacing: 4) {
                    ForEach(labels.indices, id: \.self) { index in
                        Text(labels[index])
                            .font(.caption2)
                            .foregroundColor(.secondary)
                            .frame(maxWidth: .infinity)
                    }
                }
            }
        }
    }
}

// MARK: - Combined Category Chart

struct CombinedCategoryChartView: View {
    let series: [CategorySeries]
    let labels: [String]
    let goal: GoalSummary?
    var currencyCode: String = "EUR"

    private var pointsCount: Int {
        series.first?.points.count ?? 0
    }

    private var totals: [Decimal] {
        guard pointsCount > 0 else { return [] }
        return (0..<pointsCount).map { index in
            series.reduce(Decimal.zero) { $0 + $1.points[index] }
        }
    }

    private var maxValue: Double {
        let seriesMax = totals.map { NSDecimalNumber(decimal: $0).doubleValue }.max() ?? 0
        let goalValue = goal.map { NSDecimalNumber(decimal: $0.targetAmount).doubleValue } ?? 0
        return max(seriesMax, goalValue)
    }

    private func normalized(_ value: Double) -> CGFloat {
        let maxV = maxValue
        if maxV == 0 { return 0 }
        return CGFloat(value / maxV)
    }

    var body: some View {
        HStack(alignment: .center, spacing: 6) {
            VStack(alignment: .leading, spacing: 2) {
                Text(Decimal(maxValue).shortCurrencyString(currencyCode: currencyCode))
                    .font(.caption2)
                    .foregroundColor(.secondary)
                Spacer()
                Text(Decimal(0).shortCurrencyString(currencyCode: currencyCode))
                    .font(.caption2)
                    .foregroundColor(.secondary)
            }

            VStack(spacing: 4) {
                GeometryReader { geo in
                    let height = geo.size.height
                    let width = geo.size.width
                    let columnWidth = pointsCount > 0 ? (width / CGFloat(pointsCount)) : width

                    ZStack {
                        HStack(alignment: .bottom, spacing: 4) {
                            ForEach(0..<pointsCount, id: \.self) { index in
                                let total = totals.indices.contains(index) ? totals[index] : .zero
                                let totalValue = NSDecimalNumber(decimal: total).doubleValue
                                let columnHeight = height * normalized(totalValue)

                                VStack(spacing: 1) {
                                    ForEach(series.indices, id: \.self) { catIndex in
                                        let value = series[catIndex].points[index]
                                        let valueDouble = NSDecimalNumber(decimal: value).doubleValue
                                        let segmentHeight = totalValue == 0 ? 0 : height * CGFloat(valueDouble / maxValue)
                                        RoundedRectangle(cornerRadius: 2)
                                            .fill(Color(hex: series[catIndex].color) ?? .gray)
                                            .frame(height: segmentHeight)
                                    }
                                }
                                .frame(width: columnWidth, height: columnHeight, alignment: .bottom)
                            }
                        }

                        Path { path in
                            guard totals.count > 1 else { return }
                            let step = pointsCount > 1 ? width / CGFloat(pointsCount - 1) : width
                            for (index, total) in totals.enumerated() {
                                let value = NSDecimalNumber(decimal: total).doubleValue
                                let x = CGFloat(index) * step
                                let y = height - (normalized(value) * height)
                                if index == 0 {
                                    path.move(to: CGPoint(x: x, y: y))
                                } else {
                                    path.addLine(to: CGPoint(x: x, y: y))
                                }
                            }
                        }
                        .stroke(widgetPrimaryColor.opacity(0.85), style: StrokeStyle(lineWidth: 1.6))

                        if let goal, maxValue > 0 {
                            let goalValue = NSDecimalNumber(decimal: goal.targetAmount).doubleValue
                            let goalY = height - (normalized(goalValue) * height)
                            Path { path in
                                path.move(to: CGPoint(x: 0, y: goalY))
                                path.addLine(to: CGPoint(x: width, y: goalY))
                            }
                            .stroke(widgetSecondaryColor.opacity(0.6), style: StrokeStyle(lineWidth: 1, dash: [4, 3]))
                        }
                    }
                }

                HStack(spacing: 4) {
                    ForEach(labels.indices, id: \.self) { index in
                        Text(labels[index])
                            .font(.caption2)
                            .foregroundColor(.secondary)
                            .frame(maxWidth: .infinity)
                    }
                }
            }
        }
    }
}

// MARK: - Widget Configuration

struct InvestmentWidget: Widget {
    let kind: String = "InvestmentWidget"

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: InvestmentWidgetProvider()) { entry in
            InvestmentWidgetEntryView(entry: entry)
        }
        .configurationDisplayName("Portfolio Value")
        .description("View your total investment portfolio value.")
        .supportedFamilies([
            .systemSmall,
            .systemMedium,
            .systemLarge,
            .accessoryCircular,
            .accessoryRectangular
        ])
    }
}

// MARK: - Widget Entry View

struct InvestmentWidgetEntryView: View {
    @Environment(\.widgetFamily) var family
    let entry: InvestmentWidgetEntry

    var body: some View {
        switch family {
        case .systemSmall:
            SmallWidgetView(entry: entry)
                .widgetURL(URL(string: "portfoliojournal://quickupdate")!)
        case .systemMedium:
            MediumWidgetView(entry: entry)
                .widgetURL(URL(string: "portfoliojournal://quickupdate")!)
        case .systemLarge:
            LargeWidgetView(entry: entry)
                .widgetURL(URL(string: "portfoliojournal://quickupdate")!)
        case .accessoryCircular:
            AccessoryCircularView(entry: entry)
                .widgetURL(URL(string: "portfoliojournal://quickupdate")!)
        case .accessoryRectangular:
            AccessoryRectangularView(entry: entry)
                .widgetURL(URL(string: "portfoliojournal://quickupdate")!)
        default:
            SmallWidgetView(entry: entry)
                .widgetURL(URL(string: "portfoliojournal://quickupdate")!)
        }
    }
}

// MARK: - Widget Bundle

@main
struct PortfolioJournalWidgetBundle: WidgetBundle {
    var body: some Widget {
        InvestmentWidget()
    }
}

// MARK: - Previews

#Preview("Small", as: .systemSmall) {
    InvestmentWidget()
} timeline: {
        InvestmentWidgetEntry(
            date: Date(),
            isPremium: true,
            totalValue: 50000,
            dayChange: 250,
            dayChangePercentage: 0.5,
            topSources: [],
            trendPoints: [45000, 46000, 47000, 48000, 49000, 50000],
            trendLabels: ["Aug", "Sep", "Oct", "Nov", "Dec", "Jan"],
            categoryEvolution: [],
            categoryTotals: [],
            goals: [],
            currencyCode: "EUR",
            insightTitle: "",
            insightValue: ""
        )
}

#Preview("Medium", as: .systemMedium) {
    InvestmentWidget()
} timeline: {
        InvestmentWidgetEntry(
            date: Date(),
            isPremium: true,
            totalValue: 50000,
            dayChange: 250,
            dayChangePercentage: 0.5,
            topSources: [
                ("Stocks", 30000, "#10B981"),
                ("Bonds", 15000, "#3B82F6"),
                ("Real Estate", 5000, "#F59E0B")
            ],
            trendPoints: [45000, 46000, 47000, 48000, 49000, 50000],
            trendLabels: ["Aug", "Sep", "Oct", "Nov", "Dec", "Jan"],
            categoryEvolution: [],
            categoryTotals: [],
            goals: [],
            currencyCode: "EUR",
            insightTitle: "Year to date",
            insightValue: "+12.4%"
        )
}

#Preview("Large", as: .systemLarge) {
    InvestmentWidget()
} timeline: {
        InvestmentWidgetEntry(
            date: Date(),
            isPremium: true,
            totalValue: 95000,
            dayChange: 850,
            dayChangePercentage: 0.9,
            topSources: [
                ("Vanguard", 42000, "#10B981"),
                ("Bonds", 26000, "#3B82F6"),
                ("Real Estate", 18000, "#F59E0B")
            ],
            trendPoints: [88000, 89000, 90000, 91500, 93000, 94000, 95000],
            trendLabels: ["Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Jan"],
            categoryEvolution: [
                CategorySeries(
                    id: "stocks",
                    name: "Stocks",
                    color: "#10B981",
                points: [30000, 31000, 32000, 33000, 34000, 35000, 36000],
                latestValue: 36000
            ),
            CategorySeries(
                id: "bonds",
                name: "Bonds",
                color: "#3B82F6",
                points: [22000, 22000, 23000, 24000, 25000, 25500, 26000],
                latestValue: 26000
            ),
            CategorySeries(
                id: "realestate",
                name: "Real Estate",
                color: "#F59E0B",
                points: [15000, 15500, 16000, 16500, 17000, 17500, 18000],
                latestValue: 18000
            )
            ],
            categoryTotals: [
                ("Stocks", 36000, "#10B981"),
                ("Bonds", 26000, "#3B82F6"),
                ("Real Estate", 18000, "#F59E0B")
            ],
            goals: [
                GoalSummary(name: "Target", targetAmount: 120000, targetDate: nil)
            ],
            currencyCode: "EUR",
            insightTitle: "Milestone ahead",
            insightValue: "€5K from €100K"
        )
}
extension Decimal {
    func compactCurrencyString(currencyCode: String) -> String {
        let absValue = (self as NSDecimalNumber).doubleValue.magnitude
        let sign = (self as NSDecimalNumber).doubleValue < 0 ? -1.0 : 1.0

        let (divisor, suffix): (Double, String)
        switch absValue {
        case 1_000_000_000_000...:
            (divisor, suffix) = (1_000_000_000_000, "T")
        case 1_000_000_000...:
            (divisor, suffix) = (1_000_000_000, "B")
        case 1_000_000...:
            (divisor, suffix) = (1_000_000, "M")
        case 1_000...:
            (divisor, suffix) = (1_000, "K")
        default:
            (divisor, suffix) = (1, "")
        }

        let value = (self as NSDecimalNumber).doubleValue / divisor
        let formatter = NumberFormatter()
        formatter.numberStyle = .currency
        formatter.currencyCode = currencyCode
        formatter.maximumFractionDigits = value < 10 && suffix != "" ? 1 : 0
        formatter.minimumFractionDigits = 0

        let currencySymbol = formatter.currencySymbol ?? "€"
        let formattedNumber: String
        do {
            let nf = NumberFormatter()
            nf.numberStyle = .decimal
            nf.maximumFractionDigits = formatter.maximumFractionDigits
            nf.minimumFractionDigits = formatter.minimumFractionDigits
            formattedNumber = nf.string(from: NSNumber(value: value * sign)) ?? String(format: "%.0f", value * sign)
        }

        return "\(currencySymbol)\(formattedNumber)\(suffix)"
    }

    func shortCurrencyString(currencyCode: String) -> String {
        return compactCurrencyString(currencyCode: currencyCode)
    }

    var compactCurrencyString: String {
        return compactCurrencyString(currencyCode: "EUR")
    }

    var shortCurrencyString: String {
        return shortCurrencyString(currencyCode: "EUR")
    }
}

// MARK: - Color Helper

extension Color {
    init?(hex: String) {
        var hexString = hex
        if hexString.hasPrefix("#") {
            hexString.removeFirst()
        }

        guard hexString.count == 6,
              let hexNumber = Int(hexString, radix: 16) else { return nil }

        let red = Double((hexNumber >> 16) & 0xFF) / 255
        let green = Double((hexNumber >> 8) & 0xFF) / 255
        let blue = Double(hexNumber & 0xFF) / 255

        self.init(red: red, green: green, blue: blue)
    }
}
