import Foundation

enum CurrencyFormatter {
    static func currentCurrencyCode() -> String {
        let context = CoreDataStack.shared.viewContext
        return AppSettings.getOrCreate(in: context).currency
    }

    static func locale(for currencyCode: String?) -> Locale {
        guard let currencyCode, !currencyCode.isEmpty else { return Locale.current }
        if let match = Locale.availableIdentifiers.first(where: {
            Locale(identifier: $0).currency?.identifier == currencyCode
        }) {
            return Locale(identifier: match)
        }
        return Locale.current
    }

    static func format(_ decimal: Decimal, style: NumberFormatter.Style = .currency, maximumFractionDigits: Int = 2) -> String {
        let formatter = NumberFormatter()
        formatter.numberStyle = style
        formatter.currencyCode = currentCurrencyCode()
        formatter.maximumFractionDigits = maximumFractionDigits
        return formatter.string(from: decimal as NSDecimalNumber) ?? "\(decimal)"
    }

    static func format(
        _ decimal: Decimal,
        currencyCode: String?,
        style: NumberFormatter.Style = .currency,
        maximumFractionDigits: Int = 2,
        preferredLocale: Locale? = nil
    ) -> String {
        let formatter = NumberFormatter()
        formatter.numberStyle = style
        formatter.currencyCode = currencyCode ?? currentCurrencyCode()
        formatter.maximumFractionDigits = maximumFractionDigits
        formatter.locale = preferredLocale ?? locale(for: currencyCode)
        return formatter.string(from: decimal as NSDecimalNumber) ?? "\(decimal)"
    }

    static func symbol(for code: String) -> String {
        let formatter = NumberFormatter()
        formatter.numberStyle = .currency
        formatter.currencyCode = code
        return formatter.currencySymbol ?? code
    }

    /// Parses a user-typed numeric string accepting both `.` and `,` as decimal/grouping separators.
    /// The parser is intentionally permissive to avoid turning decimal input into huge integers.
    static func parseUserInput(_ string: String, currencySymbol: String = "") -> Decimal? {
        let stripped = string
            .replacingOccurrences(of: currencySymbol, with: "")
            .replacingOccurrences(of: "\u{00A0}", with: " ")
            .trimmingCharacters(in: .whitespacesAndNewlines)
            .filter { $0.isNumber || $0 == "." || $0 == "," || $0 == "-" }

        guard !stripped.isEmpty else { return nil }

        let isNegative = stripped.hasPrefix("-")
        let unsigned = stripped.replacingOccurrences(of: "-", with: "")
        guard !unsigned.isEmpty else { return nil }

        let normalizedUnsigned = normalizeNumericInput(unsigned)
        let normalized = isNegative ? "-\(normalizedUnsigned)" : normalizedUnsigned

        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        formatter.locale = Locale(identifier: "en_US_POSIX")
        return formatter.number(from: normalized)?.decimalValue
    }

    private static func normalizeNumericInput(_ input: String) -> String {
        let hasDot = input.contains(".")
        let hasComma = input.contains(",")

        if hasDot && hasComma {
            // Both separators present: the last one is considered decimal.
            let lastDot = input.lastIndex(of: ".")!
            let lastComma = input.lastIndex(of: ",")!
            if lastComma > lastDot {
                return input
                    .replacingOccurrences(of: ".", with: "")
                    .replacingOccurrences(of: ",", with: ".")
            }
            return input.replacingOccurrences(of: ",", with: "")
        }

        if hasDot {
            return normalizeSingleSeparator(input, separator: ".")
        }

        if hasComma {
            return normalizeSingleSeparator(input, separator: ",")
        }

        return input
    }

    private static func normalizeSingleSeparator(_ input: String, separator: Character) -> String {
        let parts = input.split(separator: separator, omittingEmptySubsequences: false)
        guard parts.count > 1 else { return input }

        if parts.count == 2 {
            // A single separator is treated as decimal (e.g. 533.595).
            return "\(parts[0]).\(parts[1])"
        }

        let integerParts = Array(parts.dropLast())
        let fractionPart = String(parts.last ?? "")

        // If all groups follow a strict thousands pattern and the last one has 3 digits,
        // prefer grouping-only interpretation (e.g. 1.234.567).
        if looksLikeGroupedThousands(integerParts), fractionPart.count == 3 {
            return input.replacingOccurrences(of: String(separator), with: "")
        }

        // Otherwise, treat the last separator as decimal and previous ones as grouping.
        let integer = integerParts.joined()
        return "\(integer).\(fractionPart)"
    }

    private static func looksLikeGroupedThousands(_ groups: [Substring]) -> Bool {
        guard let first = groups.first, !first.isEmpty, first.count <= 3 else { return false }
        guard groups.count >= 2 else { return false }
        return groups.dropFirst().allSatisfy { $0.count == 3 }
    }

    /// Formats a decimal for display in an input field (no grouping separator).
    static func formatForInput(_ decimal: Decimal, currencyCode: String) -> String {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        formatter.locale = locale(for: currencyCode)
        formatter.minimumFractionDigits = 2
        formatter.maximumFractionDigits = 2
        formatter.groupingSeparator = ""
        return formatter.string(from: decimal as NSDecimalNumber) ?? ""
    }
}
