//
//  BookkeepingViewController.swift
//  VenusKitto
//
//  Created by Neoa on 2025/8/26.
//

import Foundation
import UIKit

// MARK: - 简单模型（Cell 使用）
fileprivate struct BookItem {
    let date: Date
    let title: String
    let amount: Double  // 负数为支出，正数为收入
    let count: Int      // 次数/数量
    let petName: String         // ✅ 新增：用于 cell 上显示宠物名
}

fileprivate struct DayGroup {
    let date: Date
    let total: Double
    let items: [BookItem]
}

// MARK: - 注入的宠物（带 id）
fileprivate struct BKPet {
    let id: String
    let name: String
}

// MARK: - 网络返回模型（记账账单）
fileprivate struct BillRecordResponse: Codable {
    let code: String
    let msg: String?
    let data: BillData
}
fileprivate struct BillData: Codable {
    let petName: String
    let week: String?
    let dayExpend: String?
    let groupedData: [BillGroupDTO]
    let time: String?
    let totalCost: String
}
fileprivate struct BillGroupDTO: Codable {
    let date: String         // yyyy.MM.dd
    let week: String?
    let dayTotal: String     // 当天合计
    let items: [BillItemDTO]
}
fileprivate struct BillItemDTO: Codable {
    let id: String
    let petId: String
    let avatar: String?
    let petName: String
    let recordTypeId: String?
    let recordUrl: String?
    let title: String
    let recordDate: String?
    let content: String?
    let amount: String?      // 金额（正数）
    let note: String?
    let module: String?
}

// MARK: - BookkeepingViewController (记账页面)
final class BookkeepingViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UIPickerViewDataSource, UIPickerViewDelegate, UIPopoverPresentationControllerDelegate {

    // 顶部标题
    private let titleLabel: UILabel = {
        let l = UILabel()
        l.text = "记账"
        l.font = .systemFont(ofSize: 28, weight: .semibold)
        l.textColor = UIColor(hex: "#2B2B2B")
        return l
    }()
    
    private lazy var settingsButton: UIButton = {
        let b = UIButton(type: .system)
        if let img = UIImage(systemName: "gearshape") {
            b.setImage(img, for: .normal)
        } else {
            b.setTitle("⚙️", for: .normal)
        }
        b.tintColor = UIColor(hex: "#2B2B2B")
        b.addTarget(self, action: #selector(tapSettings), for: .touchUpInside)
        return b
    }()

    private lazy var addBarItem: UIBarButtonItem = {
        UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(tapAdd))
    }()

    // Header 子模块
    private let petsRow = UIStackView()
    private let periodRow = UIStackView()
    private let monthRow = UIStackView()
    private let monthLeft = UIButton(type: .system)
    private let monthRight = UIButton(type: .system)
    private let monthLabel = UILabel()
    private let summaryCard = SummaryCardView()

    // 分段：总 / 年 / 月
    private let totalChip = SegmentChipButton(title: "总",
                                              maskedCorners: [.layerMinXMinYCorner, .layerMinXMaxYCorner],
                                              cornerRadius: 12)
    private let yearChip  = SegmentChipButton(title: "年",
                                              maskedCorners: [],
                                              cornerRadius: 0)
    private let monthChip = SegmentChipButton(title: "月",
                                              maskedCorners: [.layerMaxXMinYCorner, .layerMaxXMaxYCorner],
                                              cornerRadius: 12)

    // 列表
    private let tableView = UITableView(frame: .zero, style: .grouped)
    // tableHeader 容器，用于在布局变化时重算高度
    private var headerContainer: UIView?
    
    // 空视图
    private lazy var emptyView: UIView = {
        let container = UIView()
        container.backgroundColor = .clear
        
        let imageView = UIImageView()
        imageView.image = UIImage(named: "emptydata") // 使用现有的图片资源
        imageView.contentMode = .scaleAspectFit
//        imageView.tintColor = UIColor(hex: "#CFCFCF")
        
        let titleLabel = UILabel()
        titleLabel.text = "暂无记账记录"
        titleLabel.font = .systemFont(ofSize: 18, weight: .medium)
        titleLabel.textColor = UIColor(hex: "#CFCFCF")
        titleLabel.textAlignment = .center
        
        let subtitleLabel = UILabel()
        subtitleLabel.text = "快去添加第一条记账记录吧～"
        subtitleLabel.font = .systemFont(ofSize: 14)
        subtitleLabel.textColor = UIColor(hex: "#CFCFCF")
        subtitleLabel.textAlignment = .center
        
        let stackView = UIStackView(arrangedSubviews: [imageView, titleLabel, subtitleLabel])
        stackView.axis = .vertical
        stackView.alignment = .center
        stackView.spacing = 16
        
        container.addSubview(stackView)
        stackView.translatesAutoresizingMaskIntoConstraints = false
        imageView.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            imageView.widthAnchor.constraint(equalToConstant: 80),
            imageView.heightAnchor.constraint(equalToConstant: 80),
            
            stackView.centerXAnchor.constraint(equalTo: container.centerXAnchor),
            stackView.centerYAnchor.constraint(equalTo: container.centerYAnchor),
            stackView.leadingAnchor.constraint(greaterThanOrEqualTo: container.leadingAnchor, constant: 40),
            stackView.trailingAnchor.constraint(lessThanOrEqualTo: container.trailingAnchor, constant: -40)
        ])
        
        container.isHidden = true
        return container
    }()

    // 数据（由外部注入，带 id）
    private var pets: [BKPet] = []
    private var selectedPetIndex: Int = 0

    // 期间：0=总 1=年 2=月（目前服务端以月为主，我们都传当前年+月）
    private var periodIndex: Int = 2
    private var currentMonth: Date = Date()

    private var dayGroups: [DayGroup] = []

    // 年份选择支持
    private var yearOptions: [Int] = []
    private var tempSelectedYear: Int = Calendar.current.component(.year, from: Date())
    private var monthOptions: [Int] = Array(1...12)
    private var tempSelectedMonth: Int = Calendar.current.component(.month, from: Date())

    /// 清空并填充宠物 Chips（避免重复添加）
    private func populatePetsRow() {
        // 先移除旧的
        petsRow.arrangedSubviews.forEach { v in
            petsRow.removeArrangedSubview(v)
            v.removeFromSuperview()
        }
        // 再添加新的
        for (i, p) in pets.enumerated() {
            let btn = ChipButton(title: p.name)
            // 设置强 hugging/压缩优先级，使按钮不被拉伸
            btn.setContentHuggingPriority(.required, for: .horizontal)
            btn.setContentCompressionResistancePriority(.required, for: .horizontal)
            btn.tag = i
            btn.addTarget(self, action: #selector(tapPet(_:)), for: .touchUpInside)
            petsRow.addArrangedSubview(btn)
        }
        // 在末尾加一个弹性占位视图，使宠物名称始终靠左
        let spacer = UIView()
        spacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
        spacer.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
        petsRow.addArrangedSubview(spacer)
        updatePetChips()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor(hex: "#FFFEFC")
        navigationItem.rightBarButtonItem = addBarItem
        buildTopBar()
        buildHeader()
        buildTable()

        // 若外部尚未注入宠物，提供一个占位按钮
        if pets.isEmpty { pets = [BKPet(id: "", name: "当前宠物")] }
        refetch() // 优先拉接口；无 petId 时回落到本地 demo
   
        // ✅ 监听“记账创建成功”的通知
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(handleBookkeepingRecordCreated(_:)),
            name: .bookkeepingRecordDidCreate,
            object: nil
        )
    }
    
    @objc private func handleBookkeepingRecordCreated(_ note: Notification) {
        // 如果通知里带了 petId，仅当前选中的宠物匹配时刷新；否则直接刷新
        if let pid = note.userInfo?["petId"] as? String, let cur = currentPetId() {
            if pid == cur {
                refetch()
            }
        } else {
            refetch()
        }
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    @objc private func tapSettings() {
        print("Settings tapped")
        let vc = SettingsViewController()
        navigationController?.pushViewController(vc, animated: true)
    }


    // 外部注入宠物列表（带 id）
    func setPets(_ list: [(id: String, name: String)], selectedIndex: Int = 0) {
        self.pets = list.map { BKPet(id: $0.id, name: $0.name) }
        self.selectedPetIndex = max(0, min(selectedIndex, pets.count - 1))

        // 重建宠物 chips（统一入口，避免重复）
        populatePetsRow()
        refetch()
    }

    private func currentPetId() -> String? {
        guard !pets.isEmpty else { return nil }
        let idx = max(0, min(selectedPetIndex, pets.count - 1))
        let pid = pets[idx].id.trimmingCharacters(in: .whitespacesAndNewlines)
        return pid.isEmpty ? nil : pid
    }

    private func buildTopBar() {
        view.addSubview(titleLabel)
        view.addSubview(settingsButton)
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        settingsButton.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 12),
            titleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
            
            settingsButton.centerYAnchor.constraint(equalTo: titleLabel.centerYAnchor),
            settingsButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
            settingsButton.heightAnchor.constraint(equalToConstant: 28),
            settingsButton.widthAnchor.constraint(equalToConstant: 28),

        ])
    }

    private func buildHeader() {
        // 宠物行
        petsRow.axis = .horizontal
        petsRow.spacing = 12
        petsRow.alignment = .center
        petsRow.distribution = .fill // 让内容按自身尺寸布局，配合尾部 spacer 左对齐
        // 如果还没填充，才初始化一次，避免在 setPets 之后重复追加
        if petsRow.arrangedSubviews.isEmpty {
            if pets.isEmpty { pets = [BKPet(id: "", name: "当前宠物")] }
            populatePetsRow()
        }

        // 期间行（总 / 年 / 月）—— 左对齐、0 间距；“年”仅左侧圆角，“月”仅右侧圆角
        periodRow.axis = .horizontal
        periodRow.spacing = 0
        periodRow.alignment = .fill
        periodRow.distribution = .fill

        // 处理点击
        totalChip.tag = 100
        totalChip.addTarget(self, action: #selector(tapPeriod(_:)), for: .touchUpInside)

        yearChip.tag = 101
        yearChip.addTarget(self, action: #selector(tapPeriod(_:)), for: .touchUpInside)

        monthChip.tag = 102
        monthChip.addTarget(self, action: #selector(tapPeriod(_:)), for: .touchUpInside)

        // "总 年 月" 作为一个零间距的分段组
        let segmentGroup = UIStackView(arrangedSubviews: [totalChip, yearChip, monthChip])
        segmentGroup.axis = .horizontal
        segmentGroup.spacing = 0
        segmentGroup.alignment = .fill
        segmentGroup.distribution = .fill
        segmentGroup.setContentHuggingPriority(.required, for: .horizontal)

        periodRow.addArrangedSubview(segmentGroup)
        // 不再在 periodRow 内添加 spacer；让外层容器控制左右分布

        updatePeriodChips()

        // 月份选择
        monthRow.axis = .horizontal
        monthRow.alignment = .center
        monthRow.spacing = 8
        monthLeft.setImage(UIImage(systemName: "chevron.left"), for: .normal)
        monthRight.setImage(UIImage(systemName: "chevron.right"), for: .normal)
        [monthLeft, monthRight].forEach { $0.tintColor = UIColor(hex: "#2B2B2B") }
        monthLeft.addTarget(self, action: #selector(prevMonth), for: .touchUpInside)
        monthRight.addTarget(self, action: #selector(nextMonth), for: .touchUpInside)
        monthLabel.font = .systemFont(ofSize: 16, weight: .semibold)
        monthLabel.textColor = UIColor(hex: "#2B2B2B")
        monthLabel.isUserInteractionEnabled = true
        let _tapMonth = UITapGestureRecognizer(target: self, action: #selector(tapMonthLabel))
        monthLabel.addGestureRecognizer(_tapMonth)
        // 推荐：hugging/压缩优先级
        monthLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
        monthLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
        monthRow.addArrangedSubview(monthLeft)
        monthRow.addArrangedSubview(monthLabel)
        monthRow.addArrangedSubview(monthRight)
        updateMonthLabel()

        // periodRow + monthRow 合并到同一行：左“总/年/月”，右“月份选择”
        let flexibleSpacer = UIView()
        flexibleSpacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
        flexibleSpacer.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)

        // 将 periodRow 只承载分段（无内部 spacer），monthRow 放右侧并保持自身大小
        monthRow.setContentHuggingPriority(.required, for: .horizontal)
        monthRow.setContentCompressionResistancePriority(.required, for: .horizontal)

        let periodMonthRow = UIStackView(arrangedSubviews: [periodRow, flexibleSpacer, monthRow])
        periodMonthRow.axis = .horizontal
        periodMonthRow.alignment = .center
        periodMonthRow.spacing = 8
        periodMonthRow.distribution = .fill

        // header 内容栈
        let headerStack = UIStackView(arrangedSubviews: [petsRow, periodMonthRow, summaryCard])
        headerStack.axis = .vertical
        headerStack.spacing = 14
        headerStack.alignment = .fill
        headerStack.isLayoutMarginsRelativeArrangement = true
        headerStack.layoutMargins = UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16)

        // 新实现：不挂到 tableHeaderView，等 viewDidLayoutSubviews
        let container = UIView(frame: CGRect(x: 0, y: 0, width: 1, height: 1))
        container.autoresizingMask = [.flexibleWidth]
        container.addSubview(headerStack)
        headerStack.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            headerStack.topAnchor.constraint(equalTo: container.topAnchor),
            headerStack.leadingAnchor.constraint(equalTo: container.leadingAnchor),
            headerStack.trailingAnchor.constraint(equalTo: container.trailingAnchor),
            headerStack.bottomAnchor.constraint(equalTo: container.bottomAnchor)
        ])
        // 仅缓存，不在此时挂到 tableHeaderView，等待尺寸可用后在 viewDidLayoutSubviews 里再挂
        self.headerContainer = container
    }

    private func buildTable() {
        tableView.backgroundColor = .clear
        tableView.separatorStyle = .none
        tableView.dataSource = self
        tableView.delegate = self
        tableView.register(ExpenseCell.self, forCellReuseIdentifier: "ExpenseCell")
        view.addSubview(tableView)
        view.addSubview(emptyView)
        
        tableView.translatesAutoresizingMaskIntoConstraints = false
        emptyView.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            tableView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 8),
            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            
            emptyView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 200),
            emptyView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            emptyView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            emptyView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
        
        tableView.estimatedRowHeight = 121
        tableView.rowHeight = UITableView.automaticDimension
    }

    // 让 tableHeader 自适应高度与宽度（用于旋转/尺寸变化）
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        guard let container = headerContainer,
              let headerStack = container.subviews.first as? UIStackView else { return }

        // 只有当 tableView 已经有了确定的宽度时才挂载/更新 header
        let width = tableView.bounds.width
        guard width > 0 else { return }

        if container.frame.width != width {
            container.frame.size.width = width
        }

        let target = CGSize(width: width, height: UIView.layoutFittingCompressedSize.height)
        let height = headerStack.systemLayoutSizeFitting(
            target,
            withHorizontalFittingPriority: .required,
            verticalFittingPriority: .fittingSizeLevel
        ).height

        if tableView.tableHeaderView == nil {
            // 首次挂载
            container.frame.size.height = height
            tableView.tableHeaderView = container
        } else {
            // 尺寸变化时更新
            if container.frame.height != height {
                container.frame.size.height = height
                tableView.tableHeaderView = container // 重新赋值以使变更生效
            }
        }
        // 避免底部与外层 TabBar 重叠，留出滚动区域
        let extraBottom: CGFloat = 24
        if tableView.contentInset.bottom != extraBottom {
            tableView.contentInset.bottom = extraBottom
            tableView.scrollIndicatorInsets.bottom = extraBottom
        }
    }

    // MARK: - 触发刷新 & 网络请求
    private func refetch() {
        guard let pid = currentPetId() else {
            // 无 petId（占位宠物），显示空视图
            showEmptyView()
            return
        }
        fetchBillRecords(petId: pid, month: currentMonth)
    }

    private func fetchBillRecords(petId: String, month: Date) {
        let yFmt = DateFormatter(); yFmt.dateFormat = "yyyy"
        let ymFmt = DateFormatter(); ymFmt.dateFormat = "yyyy.MM"

        let yearStr: String
        let yearMonthStr: String
        switch periodIndex {
        case 0:      // 总：都传空
            yearStr = ""
            yearMonthStr = ""
        case 1:      // 年：只传 year
            yearStr = yFmt.string(from: month)
            yearMonthStr = ""
        default:     // 月：同时传 year 和 yearMonth
            yearStr = yFmt.string(from: month)
            yearMonthStr = ymFmt.string(from: month)
        }

        var comps = URLComponents(string: "\(baseURL)/petRecordInfo/queryBillRecordInfoList")
        comps?.queryItems = [
            URLQueryItem(name: "petId", value: petId),
            URLQueryItem(name: "year", value: yearStr),
            URLQueryItem(name: "yearMonth", value: yearMonthStr)
        ]
        guard let url = comps?.url else { return }

        var req = URLRequest(url: url)
        req.httpMethod = "GET"
        req.setValue("application/json", forHTTPHeaderField: "Content-Type")
        if let token = UserDefaults.standard.string(forKey: "userToken") {
            req.setValue(token, forHTTPHeaderField: "Authorization")
        }

        URLSession.shared.dataTask(with: req) { [weak self] data, resp, err in
            if let http = resp as? HTTPURLResponse { print("📊 bill status: \(http.statusCode)") }
            if let err = err { print("❌ bill error: \(err)"); return }
            guard let data = data else { print("❌ bill empty data"); return }
            do {
                let decoder = JSONDecoder()
                let rsp = try decoder.decode(BillRecordResponse.self, from: data)
                if rsp.code == "200" {
                    DispatchQueue.main.async { self?.applyBillData(rsp.data) }
                } else {
                    print("📊 bill server msg: \(rsp.msg ?? "-") code=\(rsp.code)")
                }
            } catch {
                print("❌ bill decode error: \(error)")
            }
        }.resume()
    }

    private func applyBillData(_ data: BillData) {
        // 顶部汇总卡
        let yF = DateFormatter(); yF.dateFormat = "yyyy"
        let ymF = DateFormatter(); ymF.dateFormat = "yyyy.MM"
        let total = Double(data.totalCost) ?? 0
        let suffix: String
        switch periodIndex {
        case 0: suffix = "合计花费"
        case 1: suffix = "\(yF.string(from: currentMonth))花费"
        default: suffix = "\(ymF.string(from: currentMonth))花费"
        }
        summaryCard.configure(title: "\(data.petName)  \(suffix)", amount: -total)
        
        // 分组 -> dayGroups
        let df = DateFormatter(); df.dateFormat = "yyyy.MM.dd"; df.locale = Locale(identifier: "zh_CN")
        var groups: [DayGroup] = []
        for g in data.groupedData {
            let date = df.date(from: g.date) ?? Date()
            var items: [BookItem] = []
            for it in g.items {
                let amt = Double(it.amount ?? "0") ?? 0
                items.append(BookItem(date: date, title: it.title, amount: -amt, count: 1, petName: it.petName))
            }
            let dayTotal = -(Double(g.dayTotal) ?? items.reduce(0) { $0 + $1.amount })
            groups.append(DayGroup(date: date, total: dayTotal, items: items))
        }
        groups.sort { $0.date > $1.date }
        self.dayGroups = groups
        
        // 检查是否有数据，决定显示列表还是空视图
        if dayGroups.isEmpty {
            showEmptyView()
        } else {
            hideEmptyView()
        }
        
        self.tableView.reloadData()
    }

    // MARK: - Demo 数据（本地兜底）
    private func reloadDemoData() {
        let fmt = DateFormatter(); fmt.dateFormat = "yyyy-MM-dd"
        let d1 = fmt.date(from: "2025-08-25")!
        let demoPetName = pets.isEmpty ? "宠物" : pets[selectedPetIndex].name
        let items = [
            BookItem(date: d1, title: "指甲剪", amount: -10, count: 1, petName: demoPetName),
            BookItem(date: d1, title: "厕所", amount: -50, count: 1, petName: demoPetName)
        ]
        // 分组
        let groups = Dictionary(grouping: items, by: { dayOnly($0.date) })
        dayGroups = groups.keys.sorted(by: >).map { day in
            let its = groups[day]!.sorted { $0.title < $1.title }
            let total = its.reduce(0) { $0 + $1.amount }
            return DayGroup(date: day, total: total, items: its)
        }
        // 顶部汇总卡
        let monthTotal = items.reduce(0) { $0 + $1.amount }
        let petName = pets.isEmpty ? "宠物" : pets[selectedPetIndex].name
        summaryCard.configure(title: "\(petName)  \(DateFormatter.with("yyyy.MM").string(from: currentMonth))花费", amount: monthTotal)
        tableView.reloadData()
    }

    @objc private func tapAdd() {
        // TODO: push 新增账目
        print("➕ Add bookkeeping")
    }
    @objc private func tapPet(_ sender: UIButton) {
        selectedPetIndex = sender.tag
        updatePetChips()
        refetch()
    }
    @objc private func tapPeriod(_ sender: UIButton) {
        periodIndex = sender.tag - 100
        updatePeriodChips()
        updateMonthLabel()   // ← 新增：切换分段后立刻刷新“月份选择”区
        refetch()
    }
    @objc private func prevMonth() {
        guard periodIndex != 0 else { return }           // “总”下不允许切换
        if periodIndex == 1 {                             // 年视图：按年切
            currentMonth = Calendar.current.date(byAdding: .year, value: -1, to: currentMonth)!
        } else {                                          // 月视图：按月切
            currentMonth = Calendar.current.date(byAdding: .month, value: -1, to: currentMonth)!
        }
        updateMonthLabel()
        refetch()
    }

    @objc private func nextMonth() {
        guard periodIndex != 0 else { return }
        if periodIndex == 1 {
            currentMonth = Calendar.current.date(byAdding: .year, value: 1, to: currentMonth)!
        } else {
            currentMonth = Calendar.current.date(byAdding: .month, value: 1, to: currentMonth)!
        }
        updateMonthLabel()
        refetch()
    }
    
    @objc private func tapMonthLabel() {
        switch periodIndex {
        case 1:
            showYearPickerSheet()
        case 2:
            showYearMonthPickerSheet()
        default:
            break
        }
    }

    private func showYearMonthPickerSheet() {
        // 年月两列选择（不显示日）
        let cur = currentMonth
        let curY = Calendar.current.component(.year, from: cur)
        let curM = Calendar.current.component(.month, from: cur)
        // 准备年份与默认选中
        yearOptions = Array((curY - 10)...(curY + 10))
        tempSelectedYear = curY
        tempSelectedMonth = curM

        let ac = UIAlertController(title: "\n\n\n\n\n\n\n\n\n", message: nil, preferredStyle: .actionSheet)
        if let p = ac.popoverPresentationController {
            p.sourceView = monthLabel
            p.sourceRect = monthLabel.bounds
            p.permittedArrowDirections = .up
            p.delegate = self
        }
        let pv = UIPickerView()
        pv.tag = 99 // 99 表示“年月”两列
        pv.dataSource = self
        pv.delegate = self
        ac.view.addSubview(pv)
        pv.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            pv.centerXAnchor.constraint(equalTo: ac.view.centerXAnchor),
            pv.topAnchor.constraint(equalTo: ac.view.topAnchor, constant: 8),
            pv.widthAnchor.constraint(equalTo: ac.view.widthAnchor)
        ])
        if let yIdx = yearOptions.firstIndex(of: curY) { pv.selectRow(yIdx, inComponent: 0, animated: false) }
        pv.selectRow(curM - 1, inComponent: 1, animated: false)

        ac.addAction(UIAlertAction(title: "确定", style: .default, handler: { [weak self] _ in
            guard let self = self else { return }
            let newDate = Calendar.current.date(from: DateComponents(year: self.tempSelectedYear, month: self.tempSelectedMonth, day: 1)) ?? self.currentMonth
            self.currentMonth = newDate
            self.updateMonthLabel()
            self.refetch()
        }))
        ac.addAction(UIAlertAction(title: "取消", style: .cancel))
        present(ac, animated: true)
    }

    private func showYearPickerSheet() {
        // 备选年份（当前年±10年）
        let curY = Calendar.current.component(.year, from: currentMonth)
        let start = curY - 10
        let end = curY + 10
        yearOptions = Array(start...end)
        tempSelectedYear = curY

        let ac = UIAlertController(title: "\n\n\n\n\n\n\n\n\n", message: nil, preferredStyle: .actionSheet)
        if let p = ac.popoverPresentationController {
            p.sourceView = monthLabel
            p.sourceRect = monthLabel.bounds
            p.permittedArrowDirections = .up
            p.delegate = self
        }
        let pv = UIPickerView()
        pv.tag = 98 // 98 表示“仅年份”一列
        pv.dataSource = self
        pv.delegate = self
        if let idx = yearOptions.firstIndex(of: curY) { pv.selectRow(idx, inComponent: 0, animated: false) }
        ac.view.addSubview(pv)
        pv.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            pv.centerXAnchor.constraint(equalTo: ac.view.centerXAnchor),
            pv.topAnchor.constraint(equalTo: ac.view.topAnchor, constant: 8),
            pv.widthAnchor.constraint(equalTo: ac.view.widthAnchor)
        ])
        ac.addAction(UIAlertAction(title: "确定", style: .default, handler: { [weak self] _ in
            guard let self = self else { return }
            let comps = Calendar.current.dateComponents([.month], from: self.currentMonth)
            let new = Calendar.current.date(from: DateComponents(year: self.tempSelectedYear, month: comps.month ?? 1, day: 1)) ?? self.currentMonth
            self.currentMonth = new
            self.updateMonthLabel()
            self.refetch()
        }))
        ac.addAction(UIAlertAction(title: "取消", style: .cancel))
        present(ac, animated: true)
    }

    private func updateMonthLabel() {
        switch periodIndex {
        case 0: // 总
            monthLabel.text = "总"
            monthLeft.isHidden = true
            monthRight.isHidden = true
        case 1: // 年
            let y = DateFormatter(); y.dateFormat = "yyyy"
            monthLabel.text = y.string(from: currentMonth)
            monthLeft.isHidden = false
            monthRight.isHidden = false
        default: // 月
            let f = DateFormatter(); f.dateFormat = "yyyy.MM"
            monthLabel.text = f.string(from: currentMonth)
            monthLeft.isHidden = false
            monthRight.isHidden = false
        }
    }
    
    private func updatePetChips() {
        for (i, v) in petsRow.arrangedSubviews.enumerated() {
            (v as? ChipButton)?.isOn = (i == selectedPetIndex)
        }
    }
    private func updatePeriodChips() {
        totalChip.isOn = (periodIndex == 0)
        yearChip.isOn  = (periodIndex == 1)
        monthChip.isOn = (periodIndex == 2)
    }

    // MARK: - Table
    func numberOfSections(in tableView: UITableView) -> Int { dayGroups.count }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { dayGroups[section].items.count }

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let v = DayHeaderView()
        let g = dayGroups[section]
        v.configure(date: g.date, total: g.total)
        return v
    }
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { 50 }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "ExpenseCell", for: indexPath) as! ExpenseCell
        let item = dayGroups[indexPath.section].items[indexPath.row]
        cell.configure(title: item.title, amount: item.amount, petName: item.petName)
        return cell
    }
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
    }

    func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
        return 121
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 121
    }
    
    // MARK: - UIPickerView DataSource & Delegate (Year/Year-Month)
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return pickerView.tag == 99 ? 2 : 1
    }

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        if pickerView.tag == 99 {
            return component == 0 ? yearOptions.count : monthOptions.count
        } else {
            return yearOptions.count
        }
    }

    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        if pickerView.tag == 99 {
            if component == 0 {
                return "\(yearOptions[row])年"
            } else {
                return String(format: "%02d月", monthOptions[row])
            }
        } else {
            return "\(yearOptions[row])年"
        }
    }

    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        if pickerView.tag == 99 {
            if component == 0, row >= 0, row < yearOptions.count {
                tempSelectedYear = yearOptions[row]
            } else if component == 1, row >= 0, row < monthOptions.count {
                tempSelectedMonth = monthOptions[row]
            }
        } else {
            if row >= 0 && row < yearOptions.count { tempSelectedYear = yearOptions[row] }
        }
    }
    
    // MARK: - 空视图管理
    private func showEmptyView() {
        dayGroups = []
        // 设置汇总卡为 0
        let petName = pets.isEmpty ? "宠物" : pets[selectedPetIndex].name
        let suffix: String
        let yF = DateFormatter(); yF.dateFormat = "yyyy"
        let ymF = DateFormatter(); ymF.dateFormat = "yyyy.MM"
        switch periodIndex {
        case 0: suffix = "合计花费"
        case 1: suffix = "\(yF.string(from: currentMonth))花费"
        default: suffix = "\(ymF.string(from: currentMonth))花费"
        }
        summaryCard.configure(title: "\(petName)  \(suffix)", amount: 0)
        
        emptyView.isHidden = false
        tableView.reloadData()
    }
    
    private func hideEmptyView() {
        emptyView.isHidden = true
    }

    // MARK: - UIPopoverPresentationControllerDelegate fallback for iPad popover
    func prepareForPopoverPresentation(_ popoverPresentationController: UIPopoverPresentationController) {
        if popoverPresentationController.sourceView == nil && popoverPresentationController.barButtonItem == nil {
            popoverPresentationController.sourceView = monthLabel.superview ?? view
            let base = popoverPresentationController.sourceView!
            popoverPresentationController.sourceRect = base.bounds
            popoverPresentationController.permittedArrowDirections = .up
        }
    }


}


// MARK: - 小组件
fileprivate final class ChipButton: UIButton {
    var isOn: Bool = false { didSet { refresh() } }
    private let inset = UIEdgeInsets(top: 4, left: 10, bottom: 4, right: 10)
    init(title: String) {
        super.init(frame: .zero)
        setTitle(title, for: .normal)
        titleLabel?.font = .systemFont(ofSize: 16, weight: .semibold)
        setTitleColor(UIColor(hex: "#2B2B2B"), for: .normal)
        layer.cornerRadius = 12
        contentEdgeInsets = inset
        backgroundColor = .clear
        refresh()
    }
    required init?(coder: NSCoder) { fatalError() }
    private func refresh() {
        layer.cornerRadius = 12
        if isOn {
            backgroundColor = UIColor(hex: "#FFE059")
            setTitleColor(.black, for: .normal)
        } else {
            backgroundColor = .clear
            setTitleColor(UIColor(hex: "#2B2B2B"), for: .normal)
        }
    }
}

fileprivate final class SegmentChipButton: UIButton {
    var isOn: Bool = false { didSet { refresh() } }
    private let inset = UIEdgeInsets(top: 6, left: 14, bottom: 6, right: 14)
    private let masked: CACornerMask
    private let cornerRadius: CGFloat

    init(title: String, maskedCorners: CACornerMask, cornerRadius: CGFloat = 12) {
        self.masked = maskedCorners
        self.cornerRadius = cornerRadius
        super.init(frame: .zero)
        setTitle(title, for: .normal)
        titleLabel?.font = .systemFont(ofSize: 16, weight: .semibold)
        setTitleColor(UIColor(hex: "#2B2B2B"), for: .normal)
        contentEdgeInsets = inset
        
        // 仅指定的角为圆角，其余为直角
        layer.cornerRadius = cornerRadius
        layer.maskedCorners = masked
        layer.masksToBounds = true

        backgroundColor = .clear
        refresh()
    }
    required init?(coder: NSCoder) { fatalError() }

    private func refresh() {
        layer.cornerRadius = cornerRadius
        if isOn {
            backgroundColor = UIColor(hex: "#FFE059")
            setTitleColor(.black, for: .normal)
        } else {
            backgroundColor = UIColor(hex: "#FEF3C1")
            setTitleColor(UIColor(hex: "#2B2B2B"), for: .normal)
        }
    }
}

fileprivate final class SummaryCardView: UIView {
    private let container = UIImageView()
    private let titleLabel = UILabel()
    private let amountLabel = UILabel()

    override init(frame: CGRect) {
        super.init(frame: frame)
        translatesAutoresizingMaskIntoConstraints = false
        container.backgroundColor = .white
        container.image = UIImage(named: "Home378")
        container.layer.cornerRadius = 14
//        container.layer.borderWidth = 1
//        container.layer.borderColor = UIColor(hex: "#2B2B2B").withAlphaComponent(0.15).cgColor
        container.layer.shadowColor = UIColor.black.cgColor
        container.layer.shadowOpacity = 0.06
        container.layer.shadowRadius = 6
        container.layer.shadowOffset = .init(width: 0, height: 2)

        addSubview(container)
        [titleLabel, amountLabel].forEach { container.addSubview($0); $0.translatesAutoresizingMaskIntoConstraints = false }

        titleLabel.font = .systemFont(ofSize: 14)
        titleLabel.textColor = UIColor(hex: "#2B2B2B")

        amountLabel.font = .systemFont(ofSize: 22, weight: .semibold)
        amountLabel.textColor = UIColor(hex: "#2B2B2B")
        amountLabel.textAlignment = .right

        container.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            container.heightAnchor.constraint(equalToConstant: 100),
            container.leadingAnchor.constraint(equalTo: leadingAnchor),
            container.trailingAnchor.constraint(equalTo: trailingAnchor),
            container.topAnchor.constraint(equalTo: topAnchor),
            container.bottomAnchor.constraint(equalTo: bottomAnchor),

            titleLabel.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 16),
            titleLabel.centerYAnchor.constraint(equalTo: container.centerYAnchor),

            amountLabel.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -16),
            amountLabel.centerYAnchor.constraint(equalTo: container.centerYAnchor)
        ])
    }
    required init?(coder: NSCoder) { fatalError() }
    func configure(title: String, amount: Double) {
        titleLabel.text = title
        amountLabel.text = String(format: "¥ %.2f", abs(amount))
    }
}

fileprivate final class DayHeaderView: UIView {
    private let dateLabel = UILabel()
    private let totalLabel = UILabel()
    private let under = UIImageView()
    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .clear
        dateLabel.font = .systemFont(ofSize: 13, weight: .semibold)
        dateLabel.textColor = UIColor(hex: "#2B2B2B")
        totalLabel.font = .systemFont(ofSize: 13)
        totalLabel.textColor = UIColor(hex: "#2B2B2B")
//        under.backgroundColor = UIColor(hex: "#FFE059")
//        under.layer.cornerRadius = 2
        under.image = UIImage(named: "Bookkeep395")

        addSubview(dateLabel); addSubview(totalLabel); addSubview(under)
        dateLabel.translatesAutoresizingMaskIntoConstraints = false
        totalLabel.translatesAutoresizingMaskIntoConstraints = false
        under.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            dateLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
            dateLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
            totalLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
            totalLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
            under.heightAnchor.constraint(equalToConstant: 12),
//            under.widthAnchor.constraint(equalToConstant: 60),
            under.topAnchor.constraint(equalTo: dateLabel.bottomAnchor, constant: -5),
            under.leadingAnchor.constraint(equalTo: dateLabel.leadingAnchor),
            under.trailingAnchor.constraint(equalTo: dateLabel.trailingAnchor)

        ])
    }
    required init?(coder: NSCoder) { fatalError() }
    func configure(date: Date, total: Double) {
        let f1 = DateFormatter(); f1.dateFormat = "yyyy.MM.dd EEEE"; f1.locale = Locale(identifier: "zh_CN")
        dateLabel.text = f1.string(from: date)
        totalLabel.text = String(format: "支出: ¥ %.2f", abs(total))
    }
}


fileprivate final class PetBadgeView: UIView {
    private let iconView = UIImageView()
    private let nameLabel = UILabel()
    private let container = UIView()

    override init(frame: CGRect) {
        super.init(frame: frame)
        // 带描边的圆角白底
        container.backgroundColor = .white
        container.layer.cornerRadius = 7
        container.layer.masksToBounds = true
        container.layer.borderWidth = 1
        container.layer.borderColor = UIColor(hex: "#5B4227").cgColor
        addSubview(container)

        iconView.contentMode = .scaleAspectFit
        iconView.layer.cornerRadius = 5
        iconView.layer.masksToBounds = true

        nameLabel.font = .systemFont(ofSize: 12)
        nameLabel.textColor = UIColor(hex: "#5B4227")

        let stack = UIStackView(arrangedSubviews: [iconView, nameLabel])
        stack.axis = .horizontal
        stack.alignment = .center
        stack.spacing = 6
        container.addSubview(stack)

        container.translatesAutoresizingMaskIntoConstraints = false
        stack.translatesAutoresizingMaskIntoConstraints = false
        iconView.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            container.topAnchor.constraint(equalTo: topAnchor),
            container.leadingAnchor.constraint(equalTo: leadingAnchor),
            container.trailingAnchor.constraint(equalTo: trailingAnchor),
            container.bottomAnchor.constraint(equalTo: bottomAnchor),

            stack.topAnchor.constraint(equalTo: container.topAnchor, constant: 4),
            stack.bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: -4),
            stack.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 4),
            stack.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -4),

            iconView.widthAnchor.constraint(equalToConstant: 10),
            iconView.heightAnchor.constraint(equalToConstant: 10)
        ])
    }

    required init?(coder: NSCoder) { fatalError() }

    func set(name: String, icon: UIImage?) {
        nameLabel.text = name
        iconView.image = icon ?? UIImage(named: "peihead")
    }
}

fileprivate final class ExpenseCell: UITableViewCell {
    // 手绘边框容器（使用切图）
    private let container = UIImageView()
    // 上半区元素
    private let icon = UIImageView(image: UIImage(named: "peihead"))
    private let titleLabel = UILabel()
    private let amountLabel = UILabel()
    // 中部整条虚线
    private let dashLayer = CAShapeLayer()
    // 右下角宠物名徽章
    private let petBadge = PetBadgeView()

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        backgroundColor = .clear
        selectionStyle = .none

        // 手绘边框底图
        container.image = UIImage(named: "Home378")
        container.contentMode = .scaleToFill
        contentView.addSubview(container)
        container.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            container.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
            container.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
            container.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8),
            container.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8),
            container.heightAnchor.constraint(greaterThanOrEqualToConstant: 121)
        ])

        // 上半区
        icon.contentMode = .scaleAspectFill
        icon.layer.cornerRadius = 8
        icon.layer.masksToBounds = true

        titleLabel.font = .systemFont(ofSize: 16, weight: .regular)
        titleLabel.textColor = UIColor(hex: "#2B2B2B")

        amountLabel.font = .systemFont(ofSize: 16, weight: .heavy)
        amountLabel.textColor = UIColor(hex: "#2B2B2B")
        amountLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
        amountLabel.textAlignment = .right

        [icon, titleLabel, amountLabel, petBadge].forEach { v in
            v.translatesAutoresizingMaskIntoConstraints = false
            container.addSubview(v)
        }

        NSLayoutConstraint.activate([
            icon.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 16),
            icon.topAnchor.constraint(equalTo: container.topAnchor, constant: 16),
            icon.widthAnchor.constraint(equalToConstant: 32),
            icon.heightAnchor.constraint(equalToConstant: 32),

            titleLabel.leadingAnchor.constraint(equalTo: icon.trailingAnchor, constant: 14),
            titleLabel.topAnchor.constraint(equalTo: icon.topAnchor,constant: 4),
            titleLabel.trailingAnchor.constraint(lessThanOrEqualTo: amountLabel.leadingAnchor, constant: -8),

            amountLabel.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -16),
            amountLabel.centerYAnchor.constraint(equalTo: titleLabel.centerYAnchor),

            petBadge.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -16),
            petBadge.bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: -14),
            petBadge.heightAnchor.constraint(greaterThanOrEqualToConstant: 14)
        ])

        // 中部整条虚线（添加在容器上）
        dashLayer.strokeColor = UIColor(hex: "#CFCFCF").cgColor
        dashLayer.fillColor = UIColor.clear.cgColor
        dashLayer.lineDashPattern = [6, 6]
        dashLayer.lineWidth = 1
        container.layer.addSublayer(dashLayer)
    }

    required init?(coder: NSCoder) { fatalError() }

    override func layoutSubviews() {
        super.layoutSubviews()
        // 让中部虚线横穿整个卡片（留左右内边距）
        let left: CGFloat = 16
        let right: CGFloat = bounds.width - 16
        // 取上半区最大底部再往下 16 作为分隔线位置
        let topMax = max(icon.frame.maxY, titleLabel.frame.maxY, amountLabel.frame.maxY)
        let y = topMax + 16
        let path = UIBezierPath()
        path.move(to: CGPoint(x: left, y: y))
        path.addLine(to: CGPoint(x: right, y: y))
        dashLayer.path = path.cgPath
    }

    func configure(title: String, amount: Double, petName: String) {
        titleLabel.text = title
        let prefix = amount < 0 ? "- " : "+ "
        amountLabel.text = String(format: "%@¥ %.2f", prefix, abs(amount))
        petBadge.set(name: petName, icon: UIImage(named: "peihead"))
    }
}


// MARK: - Utils
fileprivate func dayOnly(_ d: Date) -> Date {
    let cal = Calendar.current
    return cal.date(from: cal.dateComponents([.year, .month, .day], from: d))!
}

private extension DateFormatter {
    static func with(_ f: String) -> DateFormatter {
        let d = DateFormatter()
        d.dateFormat = f
        return d
    }
}

// MARK: - HEX 颜色
extension UIColor {
    convenience init(hex: String, alpha: CGFloat = 1.0) {
        var hexSanitized = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
        var int: UInt64 = 0
        Scanner(string: hexSanitized).scanHexInt64(&int)
        let a, r, g, b: UInt64
        switch hexSanitized.count {
        case 3: (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
        case 6: (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
        case 8: (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
        default: (a, r, g, b) = (255, 0, 0, 0)
        }
        self.init(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255)
    }
}

