//
//  HomeViewController.swift
//  VenusKitto
//
//  Created by Neoa on 2025/8/22.
//

import Foundation
import UIKit

// MARK: - HomeViewController
class HomeViewController: UIViewController, UIPopoverPresentationControllerDelegate {
    // UI
    private let scrollView = UIScrollView()
    private let contentView = UIView()

    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 let petCard = PetHeaderCard()
    private let tagScroll = UIScrollView()
    private let tagStack = UIStackView()
    private let recordStack = UIStackView()
    
    // 空视图组件
    private lazy var emptyStateView: 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
    }()

    private let addButton: UIButton = {
        let b = UIButton(type: .custom)
        b.setImage(UIImage(named: "Home357"), for: .normal)
//        b.tintColor = .black
//        b.backgroundColor = UIColor(hex: "#FFE059")
        b.layer.cornerRadius = 28
        b.layer.shadowColor = UIColor.black.cgColor
        b.layer.shadowOpacity = 0.15
        b.layer.shadowRadius = 8
        b.layer.shadowOffset = .init(width: 0, height: 4)
        b.addTarget(self, action: #selector(tapAdd), for: .touchUpInside)
        return b
    }()

    private let tabBarView = BottomTabBar()

    // --- Action menu (pop when tapping +) ---
    private var isActionMenuShown = false
    private let overlayView = UIControl()
    private let glowView = UIView()
    private let leftActionStack = UIStackView()
    private let leftActionButton = UIButton(type: .custom)
    private let leftActionLabel = UILabel()
    private let rightActionStack = UIStackView()
    private let rightActionButton = UIButton(type: .custom)
    private let rightActionLabel = UILabel()
    // Glow effect layers/animation
    private let glowGradient = CAGradientLayer()

    // --- Create Record Sheet ---
    private var createRecordSheet: CreateRecordSheet?

    // 记账分类弹窗（快速记账）
    private var bookkeepingQuickSheet: BookkeepingQuickSheet?
    
    private var bookkeepingAmountSheet: BookkeepingAmountSheet?
    
    // Embedded bookkeeping page (keeps bottom tab visible)
    private var bookkeepingVC: BookkeepingViewController?
    
    // --- Pets (list & switching) ---
    private var pets: [PetSummary] = []
    private var currentPetIndex: Int = 0
    private let imageCache = NSCache<NSString, UIImage>()
    
    // --- Empty state & add pet ---
    private var hasPet: Bool = false
    private let addPetButton: UIButton = {
        let b = UIButton(type: .system)
        b.setTitle("添加+", for: .normal)
        b.setTitleColor(.black, for: .normal)
        b.titleLabel?.font = .systemFont(ofSize: 12, weight: .medium)
//        b.contentEdgeInsets = UIEdgeInsets(top: 0, left: 18, bottom: 0, right: 18)
        return b
    }()
    private let emptyTipLabel: UILabel = {
        let l = UILabel()
        l.text = "还没有添加你的宠物哦～"
        l.font = .systemFont(ofSize: 16, weight: .medium)
        l.textColor = UIColor(hex: "#6B6B6B")
        l.textAlignment = .center
        return l
    }()
    private let emptyBanner: UIImageView = {
        let v = UIImageView()
        v.contentMode = .scaleToFill
        v.image = UIImage(named: "Home374")
        v.backgroundColor = UIColor(hex: "#FFF3D9")
        v.layer.cornerRadius = 12
        return v
    }()
    
    // MARK: - Pet names row
    private let petNamesScroll = UIScrollView()
    private let petNamesStack  = UIStackView()
    private var petNameButtons: [UIButton] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        addTopGradient()
        layout()
        populateDemo()
        setHasPet(true)
        
        fetchMemberInfo()

        // Tap pet card to switch pets
        let petTap = UITapGestureRecognizer(target: self, action: #selector(showPetPicker))
        petCard.isUserInteractionEnabled = true
        petCard.addGestureRecognizer(petTap)
    }

    private func addTopGradient() {
        // 顶部淡米色渐变 -> 白色
        let g = CAGradientLayer()
        g.colors = [UIColor(hex: "#FFF3D9").cgColor, UIColor.white.cgColor]
        g.startPoint = CGPoint(x: 0.5, y: 0.0)
        g.endPoint = CGPoint(x: 0.5, y: 0.4)
        g.frame = CGRect(x: 0, y: 0, width: view.bounds.width, height: 220)
        view.layer.insertSublayer(g, at: 0)
    }

    private func layout() {
        // Scroll
        scrollView.showsVerticalScrollIndicator = false
        view.addSubview(scrollView)
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        contentView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(contentView)

        // Header
        [titleLabel, settingsButton, petCard, tagScroll, recordStack, emptyStateView].forEach {
            $0.translatesAutoresizingMaskIntoConstraints = false
            contentView.addSubview($0)
        }

        // Empty state & add pet
        addPetButton.translatesAutoresizingMaskIntoConstraints = false
        emptyTipLabel.translatesAutoresizingMaskIntoConstraints = false
        emptyBanner.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(addPetButton)
        contentView.addSubview(emptyTipLabel)
        contentView.addSubview(emptyBanner)
        styleAddPetButton()
        addPetButton.addTarget(self, action: #selector(tapAddPet), for: .touchUpInside)
        // Tag row
        tagScroll.showsHorizontalScrollIndicator = false
        tagStack.axis = .horizontal
        tagStack.spacing = 12
        tagStack.alignment = .center
        tagScroll.addSubview(tagStack)
        tagStack.translatesAutoresizingMaskIntoConstraints = false

        // Records
        recordStack.axis = .vertical
        recordStack.spacing = 16

        // Bottom bar & add button
        view.addSubview(tabBarView)
        tabBarView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(addButton)
        addButton.translatesAutoresizingMaskIntoConstraints = false
        
        
        // —— 添加“所有宠物名字”的横向滚动条 —— //
        petNamesScroll.showsHorizontalScrollIndicator = false
        petNamesScroll.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(petNamesScroll)

        petNamesStack.axis = .horizontal
        petNamesStack.alignment = .center
        petNamesStack.spacing = 12
        petNamesStack.translatesAutoresizingMaskIntoConstraints = false
        petNamesScroll.addSubview(petNamesStack)
        
        NSLayoutConstraint.activate([
            // Scroll/Content
            scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            scrollView.bottomAnchor.constraint(equalTo: tabBarView.topAnchor),

            contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
            contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
            contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),

            // Header row
            titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 12),
            titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),

            settingsButton.centerYAnchor.constraint(equalTo: titleLabel.centerYAnchor),
            settingsButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
            settingsButton.heightAnchor.constraint(equalToConstant: 28),
            settingsButton.widthAnchor.constraint(equalToConstant: 28),

            // Add Pet Button (below titleLabel)
            addPetButton.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 8),
            addPetButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),            addPetButton.heightAnchor.constraint(equalToConstant: 26),
            addPetButton.widthAnchor.constraint(equalToConstant: 64),
            
            // Pet names row
            petNamesScroll.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 8),
            petNamesScroll.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
            petNamesScroll.trailingAnchor.constraint(equalTo: addPetButton.leadingAnchor, constant: -8),
            petNamesScroll.heightAnchor.constraint(equalToConstant: 26),

            petNamesStack.leadingAnchor.constraint(equalTo: petNamesScroll.leadingAnchor),
            petNamesStack.trailingAnchor.constraint(equalTo: petNamesScroll.trailingAnchor),
            petNamesStack.topAnchor.constraint(equalTo: petNamesScroll.topAnchor),
            petNamesStack.bottomAnchor.constraint(equalTo: petNamesScroll.bottomAnchor),

            // Pet card
            petCard.topAnchor.constraint(equalTo: petNamesScroll.bottomAnchor, constant: 17),            petCard.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 12),
            petCard.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -12),
            petCard.heightAnchor.constraint(equalToConstant: 103),
            
            // Empty tip label (below petCard)
            emptyTipLabel.topAnchor.constraint(equalTo: petCard.bottomAnchor, constant: 137),
            emptyTipLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),

            // Empty banner (below emptyTipLabel)
            emptyBanner.topAnchor.constraint(equalTo: emptyTipLabel.bottomAnchor, constant: 16),
            emptyBanner.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 24),
            emptyBanner.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -24),
            emptyBanner.heightAnchor.constraint(equalToConstant: 120),

            // Tag row
            tagScroll.topAnchor.constraint(equalTo: petCard.bottomAnchor, constant: 12),
            tagScroll.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 12),
            tagScroll.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -12),
            tagScroll.heightAnchor.constraint(equalToConstant: 38),

            tagStack.leadingAnchor.constraint(equalTo: tagScroll.leadingAnchor),
            tagStack.trailingAnchor.constraint(equalTo: tagScroll.trailingAnchor),
            tagStack.topAnchor.constraint(equalTo: tagScroll.topAnchor),
            tagStack.bottomAnchor.constraint(equalTo: tagScroll.bottomAnchor),
            tagStack.heightAnchor.constraint(equalTo: tagScroll.heightAnchor),

            // Records list
            recordStack.topAnchor.constraint(equalTo: tagScroll.bottomAnchor, constant: 12),
            recordStack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 12),
            recordStack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -12),
            recordStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -24),
            
            // Empty state view
            emptyStateView.topAnchor.constraint(equalTo: tagScroll.bottomAnchor, constant: 50),
            emptyStateView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 12),
            emptyStateView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -12),
            emptyStateView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -100),

            // Bottom tab bar
            tabBarView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            tabBarView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            tabBarView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),

            // Add button (浮在中间)
            addButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            addButton.centerYAnchor.constraint(equalTo: tabBarView.topAnchor),
            addButton.widthAnchor.constraint(equalToConstant: 56),
            addButton.heightAnchor.constraint(equalToConstant: 56)
        ])

        // Overlay (dim background)
        overlayView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
        overlayView.alpha = 0
        overlayView.isHidden = true
        overlayView.addTarget(self, action: #selector(hideActionMenu), for: .touchUpInside)
        view.insertSubview(overlayView, belowSubview: addButton)
        overlayView.translatesAutoresizingMaskIntoConstraints = false

        // Glow behind add button
        glowView.backgroundColor = UIColor.clear
        glowView.layer.cornerRadius = 28
        glowView.alpha = 0
        glowView.isHidden = true
        view.insertSubview(glowView, belowSubview: addButton)
        glowView.translatesAutoresizingMaskIntoConstraints = false
        // Configure glow gradient & shadow
        glowView.backgroundColor = .clear
        glowGradient.type = .radial
        // Neon glow color strictly FFE059
        glowGradient.colors = [
            UIColor(hex: "#FFE059").withAlphaComponent(0.95).cgColor,
            UIColor(hex: "#FFE059").withAlphaComponent(0.45).cgColor,
            UIColor(hex: "#FFE059").withAlphaComponent(0.0).cgColor
        ]
        glowGradient.locations = [0.0, 0.6, 1.0]
        // Keep glow gradient sized to the glowView
        glowGradient.frame = glowView.bounds
        glowGradient.cornerRadius = glowView.bounds.width/2
        glowView.layer.cornerRadius = glowView.bounds.width/2
        glowGradient.startPoint = CGPoint(x: 0.5, y: 0.5)
        glowGradient.endPoint = CGPoint(x: 1, y: 1)
        glowGradient.frame = glowView.bounds
        glowGradient.cornerRadius = glowView.bounds.width/2
        glowView.layer.insertSublayer(glowGradient, at: 0)

        // soft outer shadow
        glowView.layer.shadowColor = UIColor(hex: "#FFE059").cgColor
        glowView.layer.shadowOpacity = 0.9
        glowView.layer.shadowRadius = 24
        glowView.layer.shadowOffset = .zero
        glowGradient.frame = glowView.bounds
        glowGradient.cornerRadius = glowView.bounds.width/2

        // Action stacks
        func makeActionStack(button: UIButton, label: UILabel, systemImage: String, title: String) -> UIStackView {
            if let img = UIImage(named: systemImage) {
                button.setImage(img.withRenderingMode(.alwaysOriginal), for: .normal)
            } else {
                #if DEBUG
                print("⚠️ Image not found: \(systemImage)")
                #endif
            }
            button.imageView?.contentMode = .scaleAspectFit
            button.tintColor = .black
            button.backgroundColor = .white
            button.layer.cornerRadius = 28
            button.layer.shadowColor = UIColor.black.cgColor
            button.layer.shadowOpacity = 0.15
            button.layer.shadowRadius = 6
            button.layer.shadowOffset = CGSize(width: 0, height: 3)
            button.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                button.widthAnchor.constraint(equalToConstant: 56),
                button.heightAnchor.constraint(equalToConstant: 56)
            ])

            label.text = title
            label.font = .systemFont(ofSize: 12, weight: .medium)
            label.textColor = .white
            label.textAlignment = .center

            let stack = UIStackView(arrangedSubviews: [button, label])
            stack.axis = .vertical
            stack.alignment = .center
            stack.spacing = 8
            stack.alpha = 0
            stack.isHidden = true
            stack.translatesAutoresizingMaskIntoConstraints = false
            return stack
        }

        let leftStack = makeActionStack(button: leftActionButton, label: leftActionLabel, systemImage: "Home359", title: "事件")
        let rightStack = makeActionStack(button: rightActionButton, label: rightActionLabel, systemImage: "Home360", title: "记账")
        // leftActionStack.addArrangedSubview(leftActionButton) // keep references populated (optional)
        // rightActionStack.addArrangedSubview(rightActionButton)
        view.addSubview(leftStack)
        view.addSubview(rightStack)

        leftActionButton.addTarget(self, action: #selector(tapQuickEvent), for: .touchUpInside)
        rightActionButton.addTarget(self, action: #selector(tapQuickNote), for: .touchUpInside)

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

            glowView.centerXAnchor.constraint(equalTo: addButton.centerXAnchor),
            glowView.centerYAnchor.constraint(equalTo: addButton.centerYAnchor),
            glowView.widthAnchor.constraint(equalToConstant: 64),
            glowView.heightAnchor.constraint(equalToConstant: 64),

            leftStack.centerXAnchor.constraint(equalTo: addButton.centerXAnchor, constant: -90),
            leftStack.bottomAnchor.constraint(equalTo: addButton.topAnchor, constant: -24),

            rightStack.centerXAnchor.constraint(equalTo: addButton.centerXAnchor, constant: 90),
            rightStack.bottomAnchor.constraint(equalTo: addButton.topAnchor, constant: -24)
        ])

        // Store stacks for later animations by tagging via accessibilityIdentifier
        leftStack.accessibilityIdentifier = "leftActionStack"
        rightStack.accessibilityIdentifier = "rightActionStack"
        // 注册宠物保存通知
        NotificationCenter.default.addObserver(self, selector: #selector(handlePetDidSave(_:)), name: .petDidSave, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(handleEventRecordCreated(_:)), name: .eventRecordDidCreate, object: nil)
    }
    
    @objc private func handleEventRecordCreated(_ note: Notification) {
        // 优先取通知里传来的 petId；否则用当前选中的宠物
        let pid = (note.userInfo?["petId"] as? String) ?? (currentPetIndex < pets.count ? pets[currentPetIndex].id : "")
        guard !pid.isEmpty else { return }
        // 刷新分类与事件记录（默认展示全部）
        fetchRecordCategories(petId: pid)
        fetchEventRecords(petId: pid, keyword: "")
    }

    
    @objc private func handlePetDidSave(_ note: Notification) {
        // Prefer userId from notification; fallback to persisted value
        let uidFromNote = note.userInfo?["userId"] as? String
        let persistedStr = UserDefaults.standard.string(forKey: "userId")
        let persistedInt = UserDefaults.standard.integer(forKey: "userId")
        let uid = uidFromNote ?? persistedStr ?? (persistedInt == 0 ? "" : String(persistedInt))
        guard !uid.isEmpty else { return }
        // 重新拉取宠物列表并刷新UI
        fetchPetsForUser(userId: uid)
    }
    
    @objc private func tapAddPet() {
        
        // 检查登录状态
        let isLoggedIn = UserDefaults.standard.bool(forKey: "isLogggedIn")
        let userToken = UserDefaults.standard.string(forKey: "userToken")
        
        // 如果没有登录状态或者没有有效的token，弹出登录界面
        if !isLoggedIn || userToken?.isEmpty != false {
            showLoginViewController()
            return
        }

        
        let vc = AddPetViewController()
        navigationController?.pushViewController(vc, animated: true)
    }
    
    private func showLoginViewController() {
        let loginVC = LoginViewController()
        let navController = UINavigationController(rootViewController: loginVC)
        navController.modalPresentationStyle = .fullScreen
        present(navController, animated: true)
    }



    private func styleAddPetButton() {
        addPetButton.backgroundColor = UIColor(hex: "#FFE059")
        addPetButton.layer.cornerRadius = 13
        addPetButton.clipsToBounds = true
        addPetButton.setTitleColor(.black, for: .normal)
        addPetButton.titleLabel?.font = .systemFont(ofSize: 12, weight: .regular)
//        addPetButton.contentEdgeInsets = UIEdgeInsets(top: 0, left: 18, bottom: 0, right: 18)
    }

    private func setHasPet(_ value: Bool) {
        hasPet = value
//        addPetButton.isHidden = value
        tagScroll.isHidden = !value
        recordStack.isHidden = !value
        emptyStateView.isHidden = value // 没有宠物时显示空视图
        emptyTipLabel.isHidden = value
        emptyBanner.isHidden = value
        if !value {
            petCard.configure(
                avatar: UIImage(named: "Home372")!,
                name: "添加我的宠物",
                breed: "品种",
                ageText: "年龄",
                daysText: "一起生活的第XX天"
            )
        }
        // If true, do not touch petCard (keep current configuration)
    }

    private func populateDemo() {
        // Pet card demo
        petCard.configure(
            avatar: UIImage(named: "Home372") ?? UIImage(systemName: "pawprint")!,
            name: "宠物名字",
            breed: "宠物品种",
            ageText: "XX岁",
            daysText: "一起生活的第XX天"
        )
        
        tabBarView.onSelect = { [weak self] idx in
            guard let self = self else { return }
            if idx == 0 {
                // 事件：显示首页，关闭记账浮层
                self.hideBookkeeping()
                let topY = -self.scrollView.adjustedContentInset.top
                self.scrollView.setContentOffset(CGPoint(x: 0, y: topY), animated: true)
            } else {
                // 记账：作为子控制器叠加在内容区域（tabbar 之上）
                self.showBookkeeping()
            }
        }
    }

    @objc private func tapSettings() {
        print("Settings tapped")
        let vc = SettingsViewController()
        navigationController?.pushViewController(vc, animated: true)
        
    }

    @objc private func tapAdd() {
        isActionMenuShown ? hideActionMenu() : showActionMenu()
    }

    private func actionStacks() -> [UIStackView] {
        return view.subviews.compactMap { ($0 as? UIStackView)?.accessibilityIdentifier?.contains("ActionStack") == true ? ($0 as! UIStackView) : nil }
    }

    private func findStack(id: String) -> UIStackView? {
        view.subviews.first { ($0 as? UIStackView)?.accessibilityIdentifier == id } as? UIStackView
    }

    private func showActionMenu() {
        guard let leftStack = findStack(id: "leftActionStack"), let rightStack = findStack(id: "rightActionStack") else { return }
        isActionMenuShown = true
        overlayView.isHidden = false
        glowView.isHidden = false
        glowGradient.frame = glowView.bounds
        leftStack.isHidden = false
        rightStack.isHidden = false
        addButton.setImage(UIImage(named: "Home358"), for: .normal)
        startGlowPulse()
        view.layoutIfNeeded()
        leftStack.transform = CGAffineTransform(translationX: 0, y: 20)
        rightStack.transform = CGAffineTransform(translationX: 0, y: 20)
        UIView.animate(withDuration: 0.22) {
            self.overlayView.alpha = 1
            self.glowView.alpha = 1
            leftStack.alpha = 1; rightStack.alpha = 1
            leftStack.transform = .identity
            rightStack.transform = .identity
        }
    }

    @objc private func hideActionMenu() {
        guard let leftStack = findStack(id: "leftActionStack"), let rightStack = findStack(id: "rightActionStack") else { return }
        UIView.animate(withDuration: 0.2, animations: {
            self.overlayView.alpha = 0
            self.glowView.alpha = 0
            leftStack.alpha = 0; rightStack.alpha = 0
            leftStack.transform = CGAffineTransform(translationX: 0, y: 10)
            rightStack.transform = CGAffineTransform(translationX: 0, y: 10)
        }, completion: { _ in
            self.overlayView.isHidden = true
            leftStack.isHidden = true
            rightStack.isHidden = true
            self.addButton.setImage(UIImage(named: "Home357"), for: .normal)
            self.isActionMenuShown = false
            self.glowView.isHidden = true
            self.glowGradient.removeAllAnimations()
            self.stopGlowPulse()
        })
    }

    @objc private func tapQuickEvent() {
        print("快速进入 随手小记")

        // 检查登录状态
        let isLoggedIn = UserDefaults.standard.bool(forKey: "isLogggedIn")
        let userToken = UserDefaults.standard.string(forKey: "userToken")
        
        // 如果没有登录状态或者没有有效的token，弹出登录界面
        if !isLoggedIn || userToken?.isEmpty != false {
            showLoginViewController()
            return
        }

        hideActionMenu()
        showCreateRecordSheet()
    }

    @objc private func tapQuickNote() {
        print("快速进入 记账")
        
        // 检查登录状态
        let isLoggedIn = UserDefaults.standard.bool(forKey: "isLogggedIn")
        let userToken = UserDefaults.standard.string(forKey: "userToken")
        
        // 如果没有登录状态或者没有有效的token，弹出登录界面
        if !isLoggedIn || userToken?.isEmpty != false {
            showLoginViewController()
            return
        }

        hideActionMenu()
        showBookkeepingQuickSheet()
    }
    
    private func startGlowPulse() {
        if glowGradient.animation(forKey: "pulse") != nil { return }
        let scale = CABasicAnimation(keyPath: "transform.scale")
        scale.fromValue = 0.95
        scale.toValue = 1.15
        scale.duration = 1.1
        scale.autoreverses = true
        scale.repeatCount = .infinity

        let opacity = CABasicAnimation(keyPath: "opacity")
        opacity.fromValue = 0.7
        opacity.toValue = 1.0
        opacity.duration = 1.1
        opacity.autoreverses = true
        opacity.repeatCount = .infinity

        let group = CAAnimationGroup()
        group.animations = [scale, opacity]
        group.duration = 1.1
        group.autoreverses = true
        group.repeatCount = .infinity
        glowGradient.add(group, forKey: "pulse")
    }

    private func stopGlowPulse() {
        glowGradient.removeAnimation(forKey: "pulse")
    }
    
    // MARK: - Embed/Remove Bookkeeping (child VC keeps bottom tab visible)
    private func showBookkeeping() {
        // 已经展示就跳过
        if let _ = bookkeepingVC { return }

        let vc = BookkeepingViewController()
        addChild(vc)
        view.insertSubview(vc.view, belowSubview: overlayView)
        vc.view.translatesAutoresizingMaskIntoConstraints = false
        // 占据安全区顶部到 tabBar 顶部之间的区域（不遮挡底部导航）
        NSLayoutConstraint.activate([
            vc.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            vc.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            vc.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            vc.view.bottomAnchor.constraint(equalTo: tabBarView.topAnchor)
        ])
        // 先确保 view 已加载，再设置宠物
        vc.loadViewIfNeeded()
        vc.setPets(pets.map { ($0.id, $0.name) }, selectedIndex: currentPetIndex)
        // keep "+"、glow、action stacks and overlay above bookkeeping
        self.view.bringSubviewToFront(self.overlayView)
        self.view.bringSubviewToFront(self.addButton)
        self.view.bringSubviewToFront(self.glowView)
        if let l = self.findStack(id: "leftActionStack") { self.view.bringSubviewToFront(l) }
        if let r = self.findStack(id: "rightActionStack") { self.view.bringSubviewToFront(r) }
        vc.didMove(toParent: self)
        bookkeepingVC = vc

        // 可选：淡入
        vc.view.alpha = 0
        UIView.animate(withDuration: 0.22) { vc.view.alpha = 1 }
    }

    private func hideBookkeeping() {
        guard let vc = bookkeepingVC else { return }
        // 可选：淡出
        UIView.animate(withDuration: 0.18, animations: { vc.view.alpha = 0 }) { _ in
            vc.willMove(toParent: nil)
            vc.view.removeFromSuperview()
            vc.removeFromParent()
            self.bookkeepingVC = nil
        }
    }
    
    // MARK: - Create Record Sheet (Custom Modal)
    private func showCreateRecordSheet() {
        if createRecordSheet == nil {
            let sheet = CreateRecordSheet()
            sheet.viewController = self
            createRecordSheet = sheet
            view.addSubview(sheet)
            sheet.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                sheet.topAnchor.constraint(equalTo: view.topAnchor),
                sheet.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                sheet.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                sheet.bottomAnchor.constraint(equalTo: view.bottomAnchor)
            ])
            // Ensure the sheet is above everything (including bookkeeping)
            self.view.bringSubviewToFront(sheet)
        }
        createRecordSheet?.show()
    }

    // MARK: - Quick Bookkeeping Sheet (弹窗)
    private func showBookkeepingQuickSheet() {
        if bookkeepingQuickSheet == nil {
            let s = BookkeepingQuickSheet()
            // 选中某个记账分类后的回调（后续你可以在这里跳转到“新增记账”编辑页、带上已选分类）
            s.onItemSelected = { [weak self] name in
                print("选择记账分类: \(name)")
                // 点击分类后，叠加展示金额输入弹窗
                self?.showBookkeepingAmountSheet(category: name)
            }
            s.onDismiss = { [weak self] in
                // 可留空；若想释放：self?.bookkeepingQuickSheet = nil
            }
            bookkeepingQuickSheet = s
            view.addSubview(s)
            s.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                s.topAnchor.constraint(equalTo: view.topAnchor),
                s.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                s.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                s.bottomAnchor.constraint(equalTo: view.bottomAnchor)
            ])
        }
        if let s = bookkeepingQuickSheet {
            // 确保在最上层（不被子控制器等覆盖）
            view.bringSubviewToFront(s)
            s.show()
        }
    }
    
    private func showBookkeepingAmountSheet(category: String) {
        if bookkeepingAmountSheet == nil {
            let s = BookkeepingAmountSheet()

            // 日期选择：优先用你的 DatePickerSheetController；没有就用内置 actionSheet
            s.onPickDate = { [weak self] current, handler in
                guard let self = self else { return }
                if let cls = NSClassFromString("DatePickerSheetController") as? UIViewController.Type,
                   let picker = (cls as? NSObject.Type)?.init() as? UIViewController {
                    // 如果你项目里的 DatePickerSheetController 对外有回调 onConfirm(Date)
                    picker.setValue({ (d: Date) in
                        handler(d)
                        picker.dismiss(animated: true)
                    }, forKey: "onConfirm")
                    picker.setValue(current, forKey: "initialDate")
                    self.present(picker, animated: true)
                } else {
                    // 兜底：系统 UIDatePicker
                    let ac = UIAlertController(title: "\n\n\n\n\n\n\n\n\n", message: nil, preferredStyle: .actionSheet)
                    ac.modalPresentationStyle = .popover
                    if let p = ac.popoverPresentationController{
                        // 锚到视图底部中间；去掉箭头，效果更像底部弹出
                        p.sourceView = self.view
                        p.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.maxY - 1, width: 1, height: 1)
                        p.permittedArrowDirections = []
                        p.delegate = self
                    }
                    
                    let dp = UIDatePicker(frame: .zero)
                    dp.datePickerMode = .date
                    if #available(iOS 13.4, *) {
                        dp.preferredDatePickerStyle = .wheels
                    } else {
                        // Fallback on earlier versions
                    }
                    dp.date = current
                    ac.view.addSubview(dp)
                    dp.translatesAutoresizingMaskIntoConstraints = false
                    NSLayoutConstraint.activate([
                        dp.centerXAnchor.constraint(equalTo: ac.view.centerXAnchor),
                        dp.topAnchor.constraint(equalTo: ac.view.topAnchor, constant: 8)
                    ])
                    ac.addAction(UIAlertAction(title: "确定", style: .default, handler: { _ in handler(dp.date) }))
                    ac.addAction(UIAlertAction(title: "取消", style: .cancel))
                    self.present(ac, animated: true)
                }
            }

            // 宠物选择：用当前拿到的 pets 做一个 ActionSheet
            s.onPickPet = { [weak self] handler in
                guard let self = self else { return }
                guard !self.pets.isEmpty else { return }
                let ac = UIAlertController(title: "选择宠物", message: nil, preferredStyle: .actionSheet)
                ac.modalPresentationStyle = .popover
                if let p = ac.popoverPresentationController{
                    p.sourceView = self.view
                    p.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.maxY - 1, width: 1, height: 1)
                    p.permittedArrowDirections = []
                    p.delegate = self
                }
                
                for p in self.pets {
                    ac.addAction(UIAlertAction(title: p.name, style: .default, handler: { _ in handler(p) }))
                }
                ac.addAction(UIAlertAction(title: "取消", style: .cancel))
                self.present(ac, animated: true)
            }

            // 保存：发POST到后端
            s.onSave = { [weak self] cate, date, pet, note, amountText in
                guard let self = self else { return }
                // 1) 解析 petId
                let current = (self.currentPetIndex >= 0 && self.currentPetIndex < self.pets.count) ? self.pets[self.currentPetIndex] : nil
                let usePet = pet ?? current
                guard let petIdStr = usePet?.id, let petId = Int(petIdStr) else {
                    print("❌ 缺少 petId，无法提交")
                    return
                }

                // 2) 金额
                let amt = Double(amountText.replacingOccurrences(of: ",", with: "")) ?? 0

                // 3) recordTypeId & 日期
                let typeId = self.recordTypeId(for: cate)
                let dateStr = self.formatSendDate(date)

                // 4) 组装请求
                guard let url = URL(string: "\(baseURL)/petRecordInfo/createRecord") else { return }
                var req = URLRequest(url: url)
                req.httpMethod = "POST"
                req.setValue("application/json", forHTTPHeaderField: "Content-Type")
                if let token = UserDefaults.standard.string(forKey: "userToken") {
                    req.setValue(token, forHTTPHeaderField: "Authorization")
                }
                let body: [String: Any] = [
                    "amount": amt,
                    "note": note,
                    "petId": petId,
                    "recordDate": dateStr,
                    "recordTypeId": typeId,
                    "title": cate,
                    "module": "account"
                ]
                do {
                    req.httpBody = try JSONSerialization.data(withJSONObject: body, options: [])
                } catch {
                    print("❌ 序列化失败: \(error)")
                    return
                }

                URLSession.shared.dataTask(with: req) { [weak self] data, resp, err in
                    if let http = resp as? HTTPURLResponse { print("📮 createRecord status: \(http.statusCode)") }
                    if let err = err { print("❌ createRecord error: \(err)"); return }
                    guard let data = data else { print("❌ createRecord 空响应"); return }
                    if let json = try? JSONSerialization.jsonObject(with: data, options: []) {
                        print("✅ createRecord 响应: \(json)")
                    }
                    // 成功后可刷新当前宠物的列表（如果你希望记账列表也依赖这个接口）
                    DispatchQueue.main.async {
                        // ✅ 通知记账页刷新（携带 petId / 日期 / 类目 / 金额）
                        NotificationCenter.default.post(
                            name: .bookkeepingRecordDidCreate,
                            object: nil,
                            userInfo: [
                                "petId": String(petId),
                                "recordDate": dateStr,
                                "title": cate,
                                "amount": amt
                            ]
                        )

                    }
                }.resume()
            }

            bookkeepingAmountSheet = s
            view.addSubview(s)
            s.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                s.topAnchor.constraint(equalTo: view.topAnchor),
                s.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                s.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                s.bottomAnchor.constraint(equalTo: view.bottomAnchor)
            ])
        }

        if let s = bookkeepingAmountSheet {
            // 传入当前选中的宠物
            let current = (currentPetIndex >= 0 && currentPetIndex < pets.count) ? pets[currentPetIndex] : nil
            s.configure(category: category, currentPet: current)
            view.bringSubviewToFront(s)                 // 放到最上层
            s.show()
        }
    }

    private func formatSendDate(_ d: Date) -> String {
        let f = DateFormatter()
        f.locale = Locale(identifier: "zh_CN")
        f.dateFormat = "yyyy-MM-dd"
        return f.string(from: d)
    }

    private func recordTypeId(for title: String) -> Int {
        // 记账（module: account）映射——按后端给的 id
        // 为了兼容不同文案，做了少量同义词映射（如“草粮”→25）
        let map: [String: Int] = [
            // 食物 21~25
            "干粮": 21,
            "罐头": 22,
            "零食": 23,
            "冻干": 24,
            "草根": 25,

            // 生活 26~30
            "衣服": 26,
            "玩具": 27,
            "餐具": 28,
            "厕所": 29,
            "牵引绳": 30,

            // 治病 31~35
            "驱虫药": 31,
            "保健品": 32,
            "手术": 33,
            "药品": 34,
            "疫苗": 35,

            // 清洁 36~40
            "指甲剪": 36,
            "梳子": 37,
            "清洁用品": 38,
            "洗澡美容": 39,
            "洗护用品": 40
        ]
        return map[title] ?? 0
    }
    
    // MARK: - Record Categories (single-select with "全部")
    private var currentCategories: [String] = []
    private var categoryPills: [DottedPill] = []
    private var selectedCategoryIndex: Int = 0 // 0 == 全部

    // MARK: - Member Info Networking
    private func fetchMemberInfo() {
        guard let url = URL(string: "\(baseURL)/petRecordApUser/info") else { return }
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        if let token = UserDefaults.standard.string(forKey: "userToken") {
            request.setValue(token, forHTTPHeaderField: "Authorization")
        }

        let task = URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
            if let error = error {
                print("Error: \(error)")
                return
            }
            guard let data = data else { return }

            do {
                let decoder = JSONDecoder()
                let responseObject = try decoder.decode(MemberInfoResponse.self, from: data)
                if responseObject.code == "200" {
                    // Store the data
                    self?.storeMemberInfo(responseObject.data)
                } else {
                    print("Error: \(responseObject.msg ?? "Unknown error")")
                }
            } catch {
                print("Error decoding response: \(error)")
                
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
                    // 切换到登录界面
//                    self?.switchToLoginScreen()
                }
                
            }
        }
        task.resume()
    }
    
    private func switchToLoginScreen() {
        
        // 清除用户登录状态
        UserDefaults.standard.set(false, forKey: "isLogggedIn")
        UserDefaults.standard.removeObject(forKey: "userToken")

//        // 创建登录界面
        let loginVC = LoginViewController()
        let navController = UINavigationController(rootViewController: loginVC)
        navController.modalPresentationStyle = .fullScreen
        self.present(navController, animated: true)
//
//        // 获取当前窗口
//        guard let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow }) else {
//            return
//        }
//        
//        // 设置根视图控制器为登录界面
//        window.rootViewController = navController
//        
//        // 添加切换动画
//        UIView.transition(with: window,
//                          duration: 0.4,
//                          options: .transitionCrossDissolve,
//                          animations: nil,
//                          completion: nil)
    }


    private func storeMemberInfo(_ member: MemberInfo) {
        
        print("会员信息：")
        print("UserID: \(member.userId)")
        print("Phone: \(member.memberPhone)")
        print("Name: \(member.memberName)")
        print("Icon: \(member.memberIcon ?? "No Icon")")
        print("Email: \(member.memberEmail ?? "No Email")")
        print("Created Time: \(member.createdTime)")
        print("Updated Time: \(member.updatedTime)")
        if let token = UserDefaults.standard.string(forKey: "userToken") {
            print("token: \(token)")
        }

        // Store data in UserDefaults or another storage solution
        UserDefaults.standard.set(member.userId, forKey: "userId")
        UserDefaults.standard.set(member.memberPhone, forKey: "memberPhone")
        UserDefaults.standard.set(member.memberName, forKey: "memberName")
        UserDefaults.standard.set(member.memberIcon, forKey: "memberIcon")
        UserDefaults.standard.set(member.memberEmail, forKey: "memberEmail")
        UserDefaults.standard.set(member.createdTime, forKey: "createdTime")
        UserDefaults.standard.set(member.updatedTime, forKey: "updatedTime")

        // After we have userId, fetch pets for this user
        DispatchQueue.main.async { [weak self] in
            self?.fetchPetsForUser(userId: member.userId)
        }
    }

    // MARK: - Pets networking & UI
    private func fetchPetsForUser(userId: String) {
        var components = URLComponents(string: "\(baseURL)/petRecordPet/queryPetList")
        components?.queryItems = [URLQueryItem(name: "userId", value: userId)]
        guard let url = components?.url else { return }

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

        print("🐾 GET: \(url.absoluteString)")

        URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
            if let http = response as? HTTPURLResponse { print("🐾 status: \(http.statusCode)") }
            if let error = error { print("🐾 pets error: \(error)"); return }
            guard let data = data else { print("🐾 pets empty data"); return }
            do {
                let decoder = JSONDecoder()
                let resp = try decoder.decode(PetListByUserResponse.self, from: data)
                if resp.code == "200" {
                    DispatchQueue.main.async {
                        self?.pets = resp.data
                        if let first = resp.data.first {
                            self?.currentPetIndex = 0
                            self?.buildPetNameChips()
                            self?.applyPet(first)
                        }
                    }
                } else {
                    print("🐾 pets server msg: \(resp.msg ?? "-") code=\(resp.code)")
                }
            } catch {
                print("🐾 pets decode error: \(error)")
            }
        }.resume()
    }

    private func applyPet(_ pet: PetSummary) {
        let breed = pet.breedName ?? "宠物品种"
        let ageText: String = {
            if let a = pet.age, !a.isEmpty { return "\(a)岁" }
            return "年龄"
        }()
        let days: String = {
            if let d = pet.togetherDays, !d.isEmpty { return "一起生活的第\(d)天" }
            return "一起生活的第XX天"
        }()

        let placeholder = UIImage(named: "Home372") ?? UIImage(systemName: "pawprint")!
        if let avatar = pet.avatar, let url = URL(string: avatar) {
            loadImage(from: url) { [weak self] img in
                self?.petCard.configure(
                    avatar: img ?? placeholder,
                    name: pet.name,
                    breed: breed,
                    ageText: ageText,
                    daysText: days
                )
            }
        } else {
            petCard.configure(
                avatar: placeholder,
                name: pet.name,
                breed: breed,
                ageText: ageText,
                daysText: days
            )
        }
        updatePetNameSelection()
        // 拉取该宠物的记录分类（填充到标签行）
        fetchRecordCategories(petId: pet.id)
        
        // 拉取事件记录列表（默认全部）
        fetchEventRecords(petId: pet.id, keyword: "")
    }

    // 拉取记录分类列表（入参 petId）并渲染到 tag 行
    private func fetchRecordCategories(petId: String) {
        var comps = URLComponents(string: "\(baseURL)/petRecordInfo/queryRecordListCategory")
        comps?.queryItems = [URLQueryItem(name: "petId", value: petId)]
        guard let url = comps?.url else { return }

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

        print("🏷️ GET: \(url.absoluteString)")
        URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
            if let http = response as? HTTPURLResponse { print("🏷️ status: \(http.statusCode)") }
            if let error = error { print("🏷️ category error: \(error)"); return }
            guard let data = data else { print("🏷️ category empty data"); return }
            do {
                let decoder = JSONDecoder()
                let resp = try decoder.decode(RecordCategoriesResponse.self, from: data)
                if resp.code == "200" {
                    DispatchQueue.main.async {
                        self?.populateTagCategories(resp.data)
                    }
                } else {
                    print("🏷️ server msg: \(resp.msg ?? "-") code=\(resp.code)")
                }
            } catch {
                print("🏷️ decode error: \(error)")
            }
        }.resume()
    }

    // 用从接口拿到的分类填充 UI
    private func populateTagCategories(_ categories: [String]) {
        // 缓存数据
        currentCategories = categories

        // 清空旧的
        categoryPills.removeAll()
        tagStack.arrangedSubviews.forEach { v in
            tagStack.removeArrangedSubview(v)
            v.removeFromSuperview()
        }

        // 在最前面加上 “全部”
        let items: [String] = ["全部"] + categories
        // 永远展示（至少有“全部”）
        tagScroll.isHidden = items.isEmpty
        guard !items.isEmpty else { return }

        // 默认选中“全部”
        selectedCategoryIndex = min(selectedCategoryIndex, items.count - 1)

        for (idx, name) in items.enumerated() {
            let pill = DottedPill(text: name)
            pill.isUserInteractionEnabled = true
            pill.tag = idx
            // 选中态样式
            pill.setSelected(idx == selectedCategoryIndex)
            // 点击切换（单选）
            let tap = UITapGestureRecognizer(target: self, action: #selector(tapCategoryPill(_:)))
            pill.addGestureRecognizer(tap)

            pill.translatesAutoresizingMaskIntoConstraints = false
            tagStack.addArrangedSubview(pill)
            categoryPills.append(pill)
        }
    }

    // MARK: - Event Records networking & UI

    private func fetchEventRecords(petId: String, keyword: String) {
        var comps = URLComponents(string: "\(baseURL)/petRecordInfo/queryEventRecordInfoList")
        comps?.queryItems = [
            URLQueryItem(name: "petId", value: petId),
            URLQueryItem(name: "keyword", value: keyword)
        ]
        guard let url = comps?.url else { return }

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

        print("🗒️ GET: \(url.absoluteString)")
        URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
            if let http = response as? HTTPURLResponse { print("🗒️ status: \(http.statusCode)") }
            if let error = error { print("🗒️ records error: \(error)"); return }
            guard let data = data else { print("🗒️ records empty data"); return }
            do {
                let decoder = JSONDecoder()
                let resp = try decoder.decode(EventRecordListResponse.self, from: data)
                if resp.code == "200" {
                    DispatchQueue.main.async { self?.renderRecords(resp.data) }
                } else {
                    print("🗒️ records server msg: \(resp.msg ?? "-") code=\(resp.code)")
                    DispatchQueue.main.async { self?.renderRecords([]) }
                }
            } catch {
                print("🗒️ records decode error: \(error)")
                DispatchQueue.main.async { self?.renderRecords([]) }
            }
        }.resume()
    }

    private func renderRecords(_ list: [EventRecord]) {
        // 清空旧列表
        recordStack.arrangedSubviews.forEach { v in
            recordStack.removeArrangedSubview(v)
            v.removeFromSuperview()
        }
        
        // 检查是否有数据，决定显示列表还是空视图
        if list.isEmpty {
            showEmptyState()
            return
        }
        
        hideEmptyState()
        
        let placeholder = UIImage(named: "peihead") ?? UIImage(systemName: "pawprint.circle")!

        for rec in list {
            let card = RecordCard()
            // 填充内容
            let dateText = formatAPIDate(rec.recordDate)
            let summary = (rec.content?.isEmpty == false) ? rec.content! : (rec.note ?? "")
            card.configure(
                avatar: placeholder,
                title: rec.title,
                date: dateText,
                badge: rec.petName,
                summary: summary
            )
            // 异步加载头像
//            if let urlStr = rec.avatar, let url = URL(string: urlStr) {
//                loadImage(from: url) { img in
//                    // 简单重配一次以更新图片
//                    card.configure(
//                        avatar: img ?? placeholder,
//                        title: rec.title,
//                        date: dateText,
//                        badge: rec.petName,
//                        summary: summary
//                    )
//                }
//            }
            recordStack.addArrangedSubview(card)
        }
    }

    // MARK: - 空视图管理
    private func showEmptyState() {
        emptyStateView.isHidden = false
        recordStack.isHidden = true
    }
    
    private func hideEmptyState() {
        emptyStateView.isHidden = true
        recordStack.isHidden = false
    }
    
    private func formatAPIDate(_ s: String?) -> String {
        guard let s = s, !s.isEmpty else { return "" }
        let inFmt = DateFormatter()
        inFmt.locale = Locale(identifier: "en_US_POSIX")
        inFmt.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX" // 例: 2025-08-26T00:00:00.000+08:00
        if let date = inFmt.date(from: s) {
            let out = DateFormatter()
            out.locale = Locale(identifier: "zh_CN")
            out.dateFormat = "yyyy.MM.dd"
            return out.string(from: date)
        }
        return ""
    }
    
    // 构建“所有宠物名字”的一排按钮
    private func buildPetNameChips() {
        // 清空旧内容
        petNameButtons.removeAll()
        petNamesStack.arrangedSubviews.forEach { v in
            petNamesStack.removeArrangedSubview(v)
            v.removeFromSuperview()
        }

        for (idx, p) in pets.enumerated() {
            let btn = UIButton(type: .system)
            btn.setTitle(p.name, for: .normal)
            btn.titleLabel?.font = .systemFont(ofSize: 16, weight: .semibold)
            btn.setTitleColor(UIColor(hex: "#2B2B2B"), for: .normal)
            btn.backgroundColor = .clear
            btn.contentEdgeInsets = UIEdgeInsets(top: 4, left: 10, bottom: 4, right: 10)
            btn.layer.cornerRadius = 6
            btn.layer.masksToBounds = true
            btn.tag = idx
            btn.addTarget(self, action: #selector(tapPetNameButton(_:)), for: .touchUpInside)

            petNamesStack.addArrangedSubview(btn)
            petNameButtons.append(btn)
        }
        updatePetNameSelection()
    }

    private func updatePetNameSelection() {
        for (i, b) in petNameButtons.enumerated() {
            let selected = (i == currentPetIndex)
            if selected {
                b.backgroundColor = UIColor(hex: "#FFE059")
                b.setTitleColor(.black, for: .normal)
            } else {
                b.backgroundColor = .clear
                b.setTitleColor(UIColor(hex: "#2B2B2B"), for: .normal)
            }
        }
    }

    @objc private func tapCategoryPill(_ g: UITapGestureRecognizer) {
        guard let pill = g.view as? DottedPill else { return }
        let idx = pill.tag
        guard idx != selectedCategoryIndex, idx >= 0, idx < categoryPills.count else { return }
        // 单选：只允许一个选中
        selectedCategoryIndex = idx
        for (i, p) in categoryPills.enumerated() {
            p.setSelected(i == selectedCategoryIndex)
        }
        // 触发记录过滤（keyword 为空表示全部）
        let keyword = (idx == 0) ? "" : currentCategories[idx - 1]
        let currentPetId = (currentPetIndex >= 0 && currentPetIndex < pets.count) ? pets[currentPetIndex].id : ""
        guard !currentPetId.isEmpty else { return }
        fetchEventRecords(petId: currentPetId, keyword: keyword)
    }
    
    @objc private func tapPetNameButton(_ sender: UIButton) {
        let idx = sender.tag
        guard idx >= 0, idx < pets.count else { return }
        currentPetIndex = idx
        applyPet(pets[idx])        // 切换卡片信息
        updatePetNameSelection()   // 同步高亮
    }
    @objc private func showPetPicker() {
        guard !pets.isEmpty else { return }
        let ac = UIAlertController(title: "选择宠物", message: nil, preferredStyle: .actionSheet)

        // ✅ iPad 必须设置 popover 锚点
        ac.modalPresentationStyle = .popover
        if let p = ac.popoverPresentationController {
            p.sourceView = petCard
            p.sourceRect = petCard.bounds
            p.permittedArrowDirections = .up
            p.delegate = self
        }

        for (idx, p) in pets.enumerated() {
            ac.addAction(UIAlertAction(title: p.name, style: .default, handler: { [weak self] _ in
                self?.currentPetIndex = idx
                self?.applyPet(p)
            }))
        }
        ac.addAction(UIAlertAction(title: "取消", style: .cancel))
        present(ac, animated: true)
    }
    
    private func loadImage(from url: URL, completion: @escaping (UIImage?) -> Void) {
        let key = url.absoluteString as NSString
        if let cached = imageCache.object(forKey: key) {
            completion(cached)
            return
        }
        URLSession.shared.dataTask(with: url) { [weak self] data, _, _ in
            var img: UIImage? = nil
            if let data = data { img = UIImage(data: data) }
            if let img = img { self?.imageCache.setObject(img, forKey: key) }
            DispatchQueue.main.async { completion(img) }
        }.resume()
    }

    // Create a struct to model the response
    struct MemberInfoResponse: Codable {
        let code: String
        let msg: String?
        let data: MemberInfo
    }

    struct MemberInfo: Codable {
        let userId: String
        let memberPhone: String
        let memberName: String
        let memberIcon: String?
        let memberEmail: String?
        let status: String?
        let createdTime: String
        let updatedTime: String
    }

    // Flexible code type (string or int)
    enum CodeValue: Codable {
        case string(String)
        case int(Int)

        init(from decoder: Decoder) throws {
            let c = try decoder.singleValueContainer()
            if let s = try? c.decode(String.self) { self = .string(s) }
            else if let i = try? c.decode(Int.self) { self = .int(i) }
            else { self = .string("") }
        }

        func equals200() -> Bool {
            switch self { case .string(let s): return s == "200"; case .int(let i): return i == 200 }
        }
        var rawString: String { switch self { case .string(let s): return s; case .int(let i): return String(i) } }
    }

    struct PetListByUserResponse: Codable {
        let code: String
        let msg: String?
        let data: [PetSummary]
    }

    struct PetSummary: Codable {
        let id: String
        let userId: String
        let name: String
        let nickname: String?
        let weight: String?
        let categoryId: String?
        let categoryName: String?
        let breedId: String?
        let breedName: String?
        let gender: String?
        let birthDate: String?
        let arrivalDate: String?
        let avatar: String?
        let age: String?
        let togetherDays: String?
    }

    // 拉取记录分类接口响应
    struct RecordCategoriesResponse: Codable {
        let code: String
        let msg: String?
        let data: [String]
    }

    // 拉取事件记录列表响应
    struct EventRecordListResponse: Codable {
        let code: String
        let msg: String?
        let data: [EventRecord]
    }

    struct EventRecord: 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: - UIPopoverPresentationControllerDelegate (fallback)
    func prepareForPopoverPresentation(_ popoverPresentationController: UIPopoverPresentationController) {
        if popoverPresentationController.sourceView == nil && popoverPresentationController.barButtonItem == nil {
            popoverPresentationController.sourceView = view
            popoverPresentationController.sourceRect = CGRect(x: view.bounds.midX, y: view.bounds.maxY - 1, width: 1, height: 1)
            popoverPresentationController.permittedArrowDirections = []
        }
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self, name: .petDidSave, object: nil)
    }
}

// MARK: - PetDidSave Notification Name
extension Notification.Name {
    static let petDidSave = Notification.Name("PetDidSave")
    // 已有类似事件通知就保留，这里新增记账创建成功通知
    static let bookkeepingRecordDidCreate = Notification.Name("BookkeepingRecordDidCreate")

}

// MARK: - PetHeaderCard
final class PetHeaderCard: UIView {

    private let container = UIImageView()
    private let avatarView = UIImageView()
    private let nameLabel = UILabel()
    private let chipStack = UIStackView()
    private let daysRibbon = PaddingLabel(insets: .init(top: 6, left: 12, bottom: 6, right: 12))

    override init(frame: CGRect) {
        super.init(frame: frame)
        translatesAutoresizingMaskIntoConstraints = false

        container.translatesAutoresizingMaskIntoConstraints = false
        container.contentMode = .scaleToFill
        container.image = UIImage(named: "Home377")
        container.layer.shadowColor = UIColor.black.cgColor
        container.layer.shadowOpacity = 0.07
        container.layer.shadowRadius = 8
        container.layer.shadowOffset = .init(width: 0, height: 2)
        addSubview(container)

        avatarView.contentMode = .scaleAspectFill
        avatarView.layer.cornerRadius = 32
        avatarView.clipsToBounds = true
        avatarView.backgroundColor = UIColor(hex: "#FFF3D9")

        nameLabel.font = .systemFont(ofSize: 15, weight: .semibold)
        nameLabel.textColor = UIColor(hex: "#2B2B2B")

        chipStack.axis = .horizontal
        chipStack.spacing = 8
        chipStack.alignment = .center

        daysRibbon.backgroundColor = UIColor(hex: "#FFE059")
        daysRibbon.textColor = .black
        daysRibbon.font = .systemFont(ofSize: 10, weight: .medium)
        daysRibbon.layer.cornerRadius = 14
        daysRibbon.layer.masksToBounds = true

        [avatarView, nameLabel, chipStack, daysRibbon].forEach {
            $0.translatesAutoresizingMaskIntoConstraints = false
            container.addSubview($0)
        }

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

            avatarView.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 12),
            avatarView.centerYAnchor.constraint(equalTo: container.centerYAnchor),
            avatarView.widthAnchor.constraint(equalToConstant: 64),
            avatarView.heightAnchor.constraint(equalToConstant: 64),

            nameLabel.topAnchor.constraint(equalTo: avatarView.topAnchor, constant: 0),
            nameLabel.leadingAnchor.constraint(equalTo: avatarView.trailingAnchor, constant: 10),
            nameLabel.trailingAnchor.constraint(lessThanOrEqualTo: daysRibbon.leadingAnchor, constant: -8),

            chipStack.leadingAnchor.constraint(equalTo: nameLabel.leadingAnchor),
            chipStack.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: 8),
            chipStack.bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: -12),

            daysRibbon.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -8),
            daysRibbon.topAnchor.constraint(equalTo: container.topAnchor, constant: 8)
        ])
    }

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

    func configure(avatar: UIImage, name: String, breed: String, ageText: String, daysText: String) {
        avatarView.image = avatar
        nameLabel.text = name
        chipStack.arrangedSubviews.forEach { $0.removeFromSuperview() }
        [breed, ageText].forEach {
            let c = SoftChip(text: $0)
            chipStack.addArrangedSubview(c)
        }
        daysRibbon.text = daysText
    }
}

// MARK: - RecordCard
final class RecordCard: UIView {

    private let container = UIImageView()
    private let iconView = UIImageView()
    private let titleLabel = UILabel()
    private let dateLabel = UILabel()
    private let badge = StrokeBadge()
    private let bubble = UIView()
    private let summaryLabel = UILabel()

    override init(frame: CGRect) {
        super.init(frame: frame)
        translatesAutoresizingMaskIntoConstraints = false

        container.translatesAutoresizingMaskIntoConstraints = false
        container.image = UIImage(named: "Home378")
        container.contentMode = .scaleToFill
        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)

        iconView.contentMode = .scaleAspectFill
        iconView.layer.cornerRadius = 0
        iconView.image = UIImage(named: "peihead")
        iconView.layer.masksToBounds = true
        iconView.backgroundColor = UIColor(hex: "#FFF3D9")

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

        dateLabel.font = .systemFont(ofSize: 12)
        dateLabel.textColor = UIColor(hex: "#8B8B8B")

        bubble.backgroundColor = .white
        bubble.layer.cornerRadius = 12

        summaryLabel.font = .systemFont(ofSize: 13)
        summaryLabel.textColor = UIColor(hex: "#6B6B6B")
        summaryLabel.numberOfLines = 1
        summaryLabel.lineBreakMode = .byTruncatingTail

        [iconView, titleLabel, dateLabel, badge, bubble].forEach {
            $0.translatesAutoresizingMaskIntoConstraints = false
            container.addSubview($0)
        }
        bubble.addSubview(summaryLabel)
        summaryLabel.translatesAutoresizingMaskIntoConstraints = false

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

            iconView.topAnchor.constraint(equalTo: container.topAnchor, constant: 17),
            iconView.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 17),
            iconView.widthAnchor.constraint(equalToConstant: 56),
            iconView.heightAnchor.constraint(equalToConstant: 56),

            titleLabel.topAnchor.constraint(equalTo: iconView.topAnchor),
            titleLabel.leadingAnchor.constraint(equalTo: iconView.trailingAnchor, constant: 10),

            badge.centerYAnchor.constraint(equalTo: titleLabel.centerYAnchor),
            badge.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -17),

            dateLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 10),
            dateLabel.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor),

            bubble.topAnchor.constraint(equalTo: iconView.bottomAnchor, constant: 10),
            bubble.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 12),
            bubble.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -12),
            bubble.bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: -12),
            bubble.heightAnchor.constraint(equalToConstant: 38),
            
            summaryLabel.leadingAnchor.constraint(equalTo: bubble.leadingAnchor, constant: 12),
            summaryLabel.trailingAnchor.constraint(equalTo: bubble.trailingAnchor, constant: -12),
            summaryLabel.centerYAnchor.constraint(equalTo: bubble.centerYAnchor)
        ])
    }

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

    func configure(avatar: UIImage, title: String, date: String, badge: String, summary: String) {
        iconView.image = avatar
        titleLabel.text = title
        dateLabel.text = date
        self.badge.setText(badge)
        summaryLabel.text = summary
    }
}

// MARK: - BottomTabBar
final class BottomTabBar: UIView {

    private let bar = UIView()
    private let leftBtn = BottomTabItem(normalImageName: "Home293", selectedImageName: "Home290", title: "事件", isSelected: true)
    private let rightBtn = BottomTabItem(normalImageName: "Home291", selectedImageName: "Home292", title: "记账")
    private let indicator = UIView()

    var onSelect: ((Int) -> Void)?

    override init(frame: CGRect) {
        super.init(frame: frame)
        translatesAutoresizingMaskIntoConstraints = false

        bar.backgroundColor = .white
        bar.layer.shadowColor = UIColor.black.cgColor
        bar.layer.shadowOpacity = 0.06
        bar.layer.shadowRadius = 6
        bar.layer.shadowOffset = .init(width: 0, height: -2)

        addSubview(bar)
        [leftBtn, rightBtn].forEach {
            bar.addSubview($0)
            $0.translatesAutoresizingMaskIntoConstraints = false
        }
        bar.translatesAutoresizingMaskIntoConstraints = false

        leftBtn.addTarget(self, action: #selector(tapLeft), for: .touchUpInside)
        rightBtn.addTarget(self, action: #selector(tapRight), for: .touchUpInside)

        indicator.backgroundColor = .black
        indicator.layer.cornerRadius = 2

        NSLayoutConstraint.activate([
            bar.topAnchor.constraint(equalTo: topAnchor),
            bar.leadingAnchor.constraint(equalTo: leadingAnchor),
            bar.trailingAnchor.constraint(equalTo: trailingAnchor),
            bar.bottomAnchor.constraint(equalTo: bottomAnchor),
            bar.heightAnchor.constraint(equalToConstant: 64),

            leftBtn.leadingAnchor.constraint(equalTo: bar.leadingAnchor, constant: 82),
            leftBtn.topAnchor.constraint(equalTo: bar.topAnchor,constant: 8),
            leftBtn.bottomAnchor.constraint(equalTo: bar.bottomAnchor, constant: -8),
            leftBtn.widthAnchor.constraint(greaterThanOrEqualToConstant: 80),

            rightBtn.trailingAnchor.constraint(equalTo: bar.trailingAnchor, constant: -82),
            rightBtn.topAnchor.constraint(equalTo: bar.topAnchor,constant: 8),
            rightBtn.bottomAnchor.constraint(equalTo: bar.bottomAnchor, constant: -8),
            rightBtn.widthAnchor.constraint(greaterThanOrEqualToConstant: 80),

        ])
    }

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

    @objc private func tapLeft() {
        leftBtn.setSelected(true)
        rightBtn.setSelected(false)
        UIView.animate(withDuration: 0.2) {
            self.indicator.center.x = self.leftBtn.center.x
        }
        onSelect?(0)
    }

    @objc private func tapRight() {
        leftBtn.setSelected(false)
        rightBtn.setSelected(true)
        UIView.animate(withDuration: 0.2) {
            self.indicator.center.x = self.rightBtn.center.x
        }
        onSelect?(1)
    }
}

final class BottomTabItem: UIControl {
    private let iconView = UIImageView()
    private let label = UILabel()
    private let normalImage: UIImage?
    private let selectedImage: UIImage?

    init(normalImageName: String, selectedImageName: String, title: String, isSelected: Bool = false) {
        self.normalImage = UIImage(named: normalImageName)?.withRenderingMode(.alwaysOriginal)
        self.selectedImage = UIImage(named: selectedImageName)?.withRenderingMode(.alwaysOriginal)
        super.init(frame: .zero)
        iconView.contentMode = .scaleAspectFit
        iconView.image = normalImage
        label.text = title
        label.font = .systemFont(ofSize: 12)
        label.textColor = UIColor(hex: "#6B6B6B")

        let stack = UIStackView(arrangedSubviews: [iconView, label])
        stack.axis = .vertical
        stack.alignment = .center
        stack.spacing = 4
        addSubview(stack)
        stack.translatesAutoresizingMaskIntoConstraints = false
        iconView.translatesAutoresizingMaskIntoConstraints = false

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

            iconView.widthAnchor.constraint(equalToConstant: 22),
            iconView.heightAnchor.constraint(equalToConstant: 22)
        ])

        // 让点击落到整个 control
        stack.isUserInteractionEnabled = false

        setSelected(isSelected)
    }

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

    func setSelected(_ sel: Bool) {
        iconView.image = sel ? selectedImage : normalImage
        label.textColor = sel ? .black : UIColor(hex: "#6B6B6B")
    }
}

// MARK: - Small Components
final class SoftChip: PaddingLabel {
    init(text: String) {
        super.init(insets: .init(top: 6, left: 12, bottom: 6, right: 12))
        self.text = text
        font = .systemFont(ofSize: 12, weight: .medium)
        textColor = UIColor(hex: "#6B6B6B")
        backgroundColor = .white
        layer.cornerRadius = 14
        layer.masksToBounds = true
        layer.shadowColor = UIColor.black.cgColor
        layer.shadowOpacity = 0.06
        layer.shadowRadius = 4
        layer.shadowOffset = .init(width: 0, height: 1)
    }
    required init?(coder: NSCoder) { fatalError() }
}

final class DottedPill: PaddingLabel {
    private let dashLayer = CAShapeLayer()
    private(set) var isOn: Bool = false

    init(text: String) {
        super.init(insets: .init(top: 8, left: 16, bottom: 8, right: 16))
        self.text = text
        font = .systemFont(ofSize: 12)
        textColor = UIColor(hex: "#5B4227")
        backgroundColor = .clear
        layer.cornerRadius = 10
        layer.masksToBounds = true

        dashLayer.strokeColor = UIColor(hex: "#5B4227").cgColor
        dashLayer.fillColor = UIColor.clear.cgColor
        dashLayer.lineDashPattern = [6, 4]
        dashLayer.lineWidth = 1
        layer.addSublayer(dashLayer)
        applySelectionStyle()
    }
    required init?(coder: NSCoder) { fatalError() }

    func setSelected(_ selected: Bool) {
        isOn = selected
        applySelectionStyle()
    }

    private func applySelectionStyle() {
        if isOn {
            backgroundColor = UIColor(hex: "#FFE059")
            textColor = .black
            dashLayer.isHidden = true
        } else {
            backgroundColor = .clear
            textColor = UIColor(hex: "#5B4227")
            dashLayer.isHidden = false
        }
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        dashLayer.frame = bounds
        dashLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: 18).cgPath
    }
}

final class StrokeBadge: PaddingLabel {
    init() {
        super.init(insets: .init(top: 4, left: 10, bottom: 4, right: 10))
        textColor = UIColor(hex: "#5B4227")
        font = .systemFont(ofSize: 12)
        layer.cornerRadius = 12
        layer.masksToBounds = true
        layer.borderWidth = 1
        layer.borderColor = UIColor(hex: "#5B4227").cgColor
    }
    required init?(coder: NSCoder) { fatalError() }
    func setText(_ t: String) { text = t }
}

// MARK: - PaddingLabel (with insets)
class PaddingLabel: UILabel {
    private var insets: UIEdgeInsets
    init(insets: UIEdgeInsets) {
        self.insets = insets
        super.init(frame: .zero)
    }
    required init?(coder: NSCoder) { fatalError() }
    override func drawText(in rect: CGRect) {
        super.drawText(in: rect.inset(by: insets))
    }
    override var intrinsicContentSize: CGSize {
        let size = super.intrinsicContentSize
        return CGSize(width: size.width + insets.left + insets.right,
                      height: size.height + insets.top + insets.bottom)
    }
}





