//
//  VKNimbusPanelController.swift (obfuscated from UserSettingsViewController)
//  VenusKitto
//
//  Obfuscated on 2025/08/27
//

import Foundation
import UIKit

// 外部兼容：仍然可以用旧名引用
typealias UserSettingsViewController = VKNimbusPanelController

final class VKNimbusPanelController: UIViewController {

    // MARK: - UI (obfuscated names)
    private var zxAvatar: UIImage?
    private var zxNick: String = "未设置"

    private let grid: UITableView = {
        let t = UITableView(frame: .zero, style: .grouped)
        t.backgroundColor = .white
        t.separatorStyle = .none
        t.showsVerticalScrollIndicator = false
        t.translatesAutoresizingMaskIntoConstraints = false
        return t
    }()

    private let exitBtn: UIButton = {
        let b = UIButton(type: .system)
        b.setTitle("退出登录", for: .normal)
        b.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
        b.setTitleColor(.black, for: .normal)
        b.backgroundColor = UIColor(hex: "#FFE059")
        b.layer.cornerRadius = 25
        b.translatesAutoresizingMaskIntoConstraints = false
        return b
    }()

    private let nukeBtn: UIButton = {
        let b = UIButton(type: .system)
        b.setTitle("注销用户", for: .normal)
        b.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
        b.setTitleColor(UIColor(hex: "#34260C"), for: .normal)
        b.backgroundColor = .clear
        b.translatesAutoresizingMaskIntoConstraints = false
        return b
    }()

    // 数据源（标题 + 默认值/占位资源名）
    private let entries: [(String, String)] = [
        ("头像修改", "Home372"),
        ("昵称修改", "未设置")
    ]

    // MARK: - Lifecycle
    override func viewDidLoad() {
        super.viewDidLoad()
        forgeUI()
        pinAutoLayout()
        primeTableKit()
        bindActions()

        navigationItem.title = "用户设置"
        navigationController?.navigationBar.tintColor = .black
        navigationItem.leftBarButtonItem = UIBarButtonItem(
            image: UIImage(systemName: "chevron.left"),
            style: .plain,
            target: self,
            action: #selector(ax_back)
        )
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        // 头像
        if let icon = UserDefaults.standard.string(forKey: "memberIcon"),
           !icon.isEmpty, let url = URL(string: icon) {
            print("[Settings] pull avatar: \(url.absoluteString)")
            DispatchQueue.global().async { [weak self] in
                if let data = try? Data(contentsOf: url), let img = UIImage(data: data) {
                    DispatchQueue.main.async { self?.zxAvatar = img; self?.grid.reloadData() }
                }
            }
        } else {
            zxAvatar = nil
            grid.reloadData()
        }

        // 昵称
        zxNick = UserDefaults.standard.string(forKey: "memberName") ?? "未设置"
        grid.reloadData()

        navigationController?.setNavigationBarHidden(false, animated: animated)
    }

    // MARK: - Build UI
    private func forgeUI() {
        view.backgroundColor = .white
        view.addSubview(grid)
        view.addSubview(exitBtn)
        view.addSubview(nukeBtn)
    }

    private func pinAutoLayout() {
        NSLayoutConstraint.activate([
            grid.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10),
            grid.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            grid.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
            grid.heightAnchor.constraint(equalToConstant: 150),

            nukeBtn.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10),
            nukeBtn.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            nukeBtn.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
            nukeBtn.heightAnchor.constraint(equalToConstant: 50),

            exitBtn.bottomAnchor.constraint(equalTo: nukeBtn.topAnchor, constant: -20),
            exitBtn.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            exitBtn.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
            exitBtn.heightAnchor.constraint(equalToConstant: 50)
        ])
    }

    private func primeTableKit() {
        grid.register(VXSettingCell.self, forCellReuseIdentifier: VXSettingCell.reuseId)
        grid.dataSource = self
        grid.delegate = self
        grid.isScrollEnabled = false
        grid.layer.cornerRadius = 8
    }

    private func bindActions() {
        exitBtn.addTarget(self, action: #selector(ax_logout), for: .touchUpInside)
        nukeBtn.addTarget(self, action: #selector(ax_deleteAccount), for: .touchUpInside)
    }

    // MARK: - Navigation
    @objc private func ax_back() {
        if presentingViewController != nil { dismiss(animated: true) }
        else { navigationController?.popViewController(animated: true) }
    }

    // MARK: - Actions
    @objc private func ax_logout() { askLogout() }
    @objc private func ax_deleteAccount() { askDestruct() }

    private func askLogout() {
        let ac = UIAlertController(title: "退出登录", message: "确定要退出当前账号吗？", preferredStyle: .alert)
        ac.addAction(UIAlertAction(title: "取消", style: .cancel))
        ac.addAction(UIAlertAction(title: "确定", style: .destructive) { _ in self.doLogout() })
        present(ac, animated: true)
    }

    private func askDestruct() {
        let ac = UIAlertController(title: "注销账户", message: "此操作将永久删除您的账户和所有数据，确定要继续吗？", preferredStyle: .alert)
        ac.addAction(UIAlertAction(title: "取消", style: .cancel))
        ac.addAction(UIAlertAction(title: "注销", style: .destructive) { _ in self.doErase() })
        present(ac, animated: true)
    }

    private func doLogout() {
        UserDefaults.standard.set(false, forKey: "isLogggedIn")
        UserDefaults.standard.removeObject(forKey: "userToken")
        vx_toast("已退出登录")
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { self.routeToLogin() }
    }

    private func doErase() {
        vx_toast("账户已注销")
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { self.routeToLogin() }
    }

    private func routeToLogin() {
        let loginVC = LoginViewController()
        let nav = UINavigationController(rootViewController: loginVC)
        guard let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow }) else { return }
        window.rootViewController = nav
        UIView.transition(with: window, duration: 0.5, options: .transitionCrossDissolve, animations: nil)
    }
}

// MARK: - Table
extension VKNimbusPanelController: UITableViewDataSource, UITableViewDelegate {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { entries.count }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: VXSettingCell.reuseId, for: indexPath) as! VXSettingCell
        let (title, val) = entries[indexPath.row]
        if indexPath.row == 0 {
            cell.bind(title: title,
                      value: "",
                      isFirst: indexPath.row == 0,
                      isLast: indexPath.row == entries.count - 1,
                      avatar: zxAvatar,
                      placeholder: val)
        } else {
            cell.bind(title: title,
                      value: zxNick,
                      isFirst: indexPath.row == 0,
                      isLast: indexPath.row == entries.count - 1,
                      avatar: nil,
                      placeholder: val)
        }
        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { indexPath.row == 0 ? 80 : 60 }
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { UIView() }
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { 0.1 }
    func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { UIView() }
    func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { 0.1 }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        switch indexPath.row {
        case 0: popAvatarPanel()
        case 1: presentNicknamePad()
        default: break
        }
    }
}

// MARK: - Avatar
extension VKNimbusPanelController: UIImagePickerControllerDelegate, UINavigationControllerDelegate, UIPopoverPresentationControllerDelegate {
    private func popAvatarPanel() {
        let ac = UIAlertController(title: "更换头像", message: nil, preferredStyle: .actionSheet)
        ac.modalPresentationStyle = .popover
        if let p = ac.popoverPresentationController {
            p.delegate = self
            let idx = IndexPath(row: 0, section: 0)
            if let cell = grid.cellForRow(at: idx) {
                p.sourceView = cell.contentView; p.sourceRect = cell.contentView.bounds
            } else {
                p.sourceView = view; p.sourceRect = CGRect(x: view.bounds.midX, y: view.bounds.maxY - 1, width: 1, height: 1)
            }
            p.permittedArrowDirections = []
        }
        if UIImagePickerController.isSourceTypeAvailable(.camera) {
            let a = UIAlertAction(title: "拍照", style: .default) { [weak self] _ in self?.summonCamera() }
            a.setValue(UIColor.black, forKey: "titleTextColor"); ac.addAction(a)
        }
        let b = UIAlertAction(title: "从相册选择", style: .default) { [weak self] _ in self?.summonLibrary() }
        b.setValue(UIColor.black, forKey: "titleTextColor"); ac.addAction(b)
        let c = UIAlertAction(title: "取消", style: .cancel)
        c.setValue(UIColor.black, forKey: "titleTextColor"); ac.addAction(c)
        present(ac, animated: true)
    }

    private func summonCamera() {
        let p = UIImagePickerController()
        p.delegate = self
        p.sourceType = .camera
        p.allowsEditing = true
        present(p, animated: true)
    }
    private func summonLibrary() {
        let p = UIImagePickerController()
        p.delegate = self
        p.sourceType = .photoLibrary
        p.allowsEditing = true
        present(p, animated: true)
    }

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        picker.dismiss(animated: true)
        guard let img = info[.editedImage] as? UIImage else { return }
        showHud()
        guard let data = squash(img) else { hideHud(); vx_toast("图片处理失败"); return }
        pushBlob(data) { [weak self] result in
            DispatchQueue.main.async {
                self?.hideHud()
                switch result {
                case .success(let url):
                    self?.zxAvatar = img
                    self?.grid.reloadData()
                    self?.syncAvatarUrl(url)
                case .failure(let e):
                    self?.raiseOops("上传失败: \(e.localizedDescription)")
                }
            }
        }
    }
    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 = []
        }
    }
}

// MARK: - Nickname
extension VKNimbusPanelController {
    private func presentNicknamePad() {
        let mask = UIView(frame: view.bounds)
        mask.backgroundColor = UIColor.black.withAlphaComponent(0.5)
        mask.alpha = 0; mask.tag = 1001
        view.addSubview(mask)

        let panel = UIView(); panel.backgroundColor = .white; panel.layer.cornerRadius = 12; panel.translatesAutoresizingMaskIntoConstraints = false
        mask.addSubview(panel)

        let title = UILabel(); title.text = "修改昵称"; title.font = .systemFont(ofSize: 16, weight: .medium); title.textAlignment = .center; title.translatesAutoresizingMaskIntoConstraints = false
        let tf = UITextField(); tf.placeholder = "请输入昵称"; tf.font = .systemFont(ofSize: 16); tf.borderStyle = .roundedRect; tf.clearButtonMode = .whileEditing; tf.text = (zxNick == "未设置" ? "" : zxNick); tf.translatesAutoresizingMaskIntoConstraints = false
        let sep = UIView(); sep.backgroundColor = UIColor(hex: "#EEEEEE"); sep.translatesAutoresizingMaskIntoConstraints = false
        let cancel = UIButton(type: .system); cancel.setTitle("取消", for: .normal); cancel.setTitleColor(UIColor(hex: "#999999"), for: .normal); cancel.translatesAutoresizingMaskIntoConstraints = false
        let ok = UIButton(type: .system); ok.setTitle("完成", for: .normal); ok.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium); ok.setTitleColor(.black, for: .normal); ok.translatesAutoresizingMaskIntoConstraints = false

        panel.addSubview(title); panel.addSubview(tf); panel.addSubview(sep); panel.addSubview(cancel); panel.addSubview(ok)

        NSLayoutConstraint.activate([
            panel.centerXAnchor.constraint(equalTo: mask.centerXAnchor),
            panel.centerYAnchor.constraint(equalTo: mask.centerYAnchor),
            panel.widthAnchor.constraint(equalToConstant: 280),
            panel.heightAnchor.constraint(equalToConstant: 180),

            title.topAnchor.constraint(equalTo: panel.topAnchor, constant: 20),
            title.leadingAnchor.constraint(equalTo: panel.leadingAnchor, constant: 20),
            title.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -20),

            tf.topAnchor.constraint(equalTo: title.bottomAnchor, constant: 20),
            tf.leadingAnchor.constraint(equalTo: panel.leadingAnchor, constant: 20),
            tf.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -20),
            tf.heightAnchor.constraint(equalToConstant: 40),

            sep.topAnchor.constraint(equalTo: tf.bottomAnchor, constant: 20),
            sep.leadingAnchor.constraint(equalTo: panel.leadingAnchor),
            sep.trailingAnchor.constraint(equalTo: panel.trailingAnchor),
            sep.heightAnchor.constraint(equalToConstant: 0.5),

            cancel.topAnchor.constraint(equalTo: sep.bottomAnchor),
            cancel.leadingAnchor.constraint(equalTo: panel.leadingAnchor),
            cancel.trailingAnchor.constraint(equalTo: panel.centerXAnchor),
            cancel.bottomAnchor.constraint(equalTo: panel.bottomAnchor),
            cancel.heightAnchor.constraint(equalToConstant: 50),

            ok.topAnchor.constraint(equalTo: sep.bottomAnchor),
            ok.leadingAnchor.constraint(equalTo: panel.centerXAnchor),
            ok.trailingAnchor.constraint(equalTo: panel.trailingAnchor),
            ok.bottomAnchor.constraint(equalTo: panel.bottomAnchor),
            ok.heightAnchor.constraint(equalToConstant: 50)
        ])

        cancel.addTarget(self, action: #selector(x_dismissOverlay), for: .touchUpInside)
        ok.addTarget(self, action: #selector(x_commitNickname), for: .touchUpInside)

        mask.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(x_dismissOverlay)))
        UIView.animate(withDuration: 0.3) { mask.alpha = 1 }
        tf.becomeFirstResponder()
    }

    @objc private func x_dismissOverlay() {
        if let mask = view.viewWithTag(1001) {
            UIView.animate(withDuration: 0.2, animations: { mask.alpha = 0 }) { _ in mask.removeFromSuperview() }
        }
    }

    @objc private func x_commitNickname() {
        guard let mask = view.viewWithTag(1001),
              let panel = mask.subviews.first,
              let tf = panel.subviews.first(where: { $0 is UITextField }) as? UITextField,
              let text = tf.text, !text.isEmpty else {
            vx_toast("昵称不能为空")
            return
        }
        rx_updateNick(text)
        x_dismissOverlay()
    }
}

// MARK: - Networking helpers
extension VKNimbusPanelController {
    private func squash(_ image: UIImage) -> Data? {
        let target = CGSize(width: 800, height: 800)
        return image.x_resize(to: target)?.jpegData(compressionQuality: 0.7)
    }

    private func showHud() {
        let indicator = UIActivityIndicatorView(style: .large)
        indicator.color = .white
        indicator.startAnimating()
        let bg = UIView(frame: view.bounds)
        bg.backgroundColor = UIColor.black.withAlphaComponent(0.5)
        bg.tag = 1002
        bg.addSubview(indicator)
        indicator.center = bg.center
        view.addSubview(bg)
    }

    private func hideHud() { view.viewWithTag(1002)?.removeFromSuperview() }

    private func pushBlob(_ imageData: Data, completion: @escaping (Result<String, Error>) -> Void) {
        guard let token = UserDefaults.standard.string(forKey: "userToken") else {
            completion(.failure(NSError(domain: "AuthError", code: 401, userInfo: [NSLocalizedDescriptionKey: "未找到用户凭证"])))
            return
        }
        guard let url = URL(string: "\(baseURL)/common/upload") else {
            completion(.failure(NSError(domain: "URLError", code: 400, userInfo: [NSLocalizedDescriptionKey: "无效的服务器地址"])))
            return
        }
        var req = URLRequest(url: url)
        req.httpMethod = "POST"
        req.setValue(token, forHTTPHeaderField: "Authorization")
        let boundary = "Boundary-\(UUID().uuidString)"
        req.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

        var body = Data()
        body.append("--\(boundary)\r\n".data(using: .utf8)!)
        body.append("Content-Disposition: form-data; name=\"file\"; filename=\"avatar.jpg\"\r\n".data(using: .utf8)!)
        body.append("Content-Type: image/jpeg\r\n\r\n".data(using: .utf8)!)
        body.append(imageData)
        body.append("\r\n".data(using: .utf8)!)
        body.append("--\(boundary)--\r\n".data(using: .utf8)!)
        req.httpBody = body
        dbg_emitRequest(name: "上传头像(文件)", request: req, body: body)

        URLSession.shared.dataTask(with: req) { [weak self] data, response, error in
            self?.dbg_emitResponse(name: "上传头像(文件)", data: data, response: response, error: error)
            if let error = error { completion(.failure(error)); return }
            guard let http = response as? HTTPURLResponse, (200...299).contains(http.statusCode), let data = data else {
                completion(.failure(NSError(domain: "ServerError", code: 500, userInfo: [NSLocalizedDescriptionKey: "服务器返回错误"])))
                return
            }
            do {
                if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
                   let codeStr: String = (json["code"] as? String) ?? ((json["code"] as? Int).map { String($0) }),
                   codeStr == "200",
                   let dataDict = json["data"] as? [String: Any],
                   let imageUrl = dataDict["url"] as? String {
                    completion(.success(imageUrl))
                } else {
                    completion(.failure(NSError(domain: "ParseError", code: 500, userInfo: [NSLocalizedDescriptionKey: "响应解析失败"])))
                }
            } catch {
                completion(.failure(error))
            }
        }.resume()
    }

    private func syncAvatarUrl(_ imageUrl: String) {
        guard let token = UserDefaults.standard.string(forKey: "userToken") else { raiseOops("未找到用户凭证"); return }
        guard let url = URL(string: "\(baseURL)/petRecordApUser/modifyMemberIcon") else { raiseOops("无效的服务器地址"); return }
        var req = URLRequest(url: url)
        req.httpMethod = "POST"
        req.setValue("application/json", forHTTPHeaderField: "Content-Type")
        req.setValue(token, forHTTPHeaderField: "Authorization")
        let payload: [String: Any] = ["memberIcon": imageUrl]
        do {
            req.httpBody = try JSONSerialization.data(withJSONObject: payload)
            dbg_emitRequest(name: "更新头像URL", request: req, body: req.httpBody)
        } catch { raiseOops("请求创建失败"); return }

        showHud()
        URLSession.shared.dataTask(with: req) { [weak self] data, response, error in
            DispatchQueue.main.async {
                self?.dbg_emitResponse(name: "更新头像URL", data: data, response: response, error: error)
                self?.hideHud()
                if let error = error { self?.raiseOops("更新失败: \(error.localizedDescription)"); return }
                guard let http = response as? HTTPURLResponse, (200...299).contains(http.statusCode), let data = data else { self?.raiseOops("服务器返回错误"); return }
                do {
                    if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
                       let codeStr: String = (json["code"] as? String) ?? ((json["code"] as? Int).map { String($0) }),
                       codeStr == "200" {
                        self?.vx_toast("头像更新成功")
                        UserDefaults.standard.set(imageUrl, forKey: "memberIcon")
                        NotificationCenter.default.post(name: Notification.Name("UserAvatarUpdated"), object: imageUrl)
                    } else {
                        self?.raiseOops("更新失败")
                    }
                } catch {
                    self?.raiseOops("响应解析失败")
                }
            }
        }.resume()
    }

    private func rx_updateNick(_ newNickname: String) {
        guard let token = UserDefaults.standard.string(forKey: "userToken") else { raiseOops("未找到用户凭证"); return }
        guard let url = URL(string: "\(baseURL)/petRecordApUser/modifyMemberName") else { raiseOops("无效的服务器地址"); return }
        var req = URLRequest(url: url)
        req.httpMethod = "POST"
        req.setValue("application/json", forHTTPHeaderField: "Content-Type")
        req.setValue(token, forHTTPHeaderField: "Authorization")
        let payload: [String: Any] = ["memberName": newNickname]
        do {
            req.httpBody = try JSONSerialization.data(withJSONObject: payload)
            dbg_emitRequest(name: "修改昵称", request: req, body: req.httpBody)
        } catch { raiseOops("请求创建失败"); return }

        showHud()
        URLSession.shared.dataTask(with: req) { [weak self] data, response, error in
            DispatchQueue.main.async {
                self?.dbg_emitResponse(name: "修改昵称", data: data, response: response, error: error)
                self?.hideHud()
                if let error = error { self?.raiseOops("更新失败: \(error.localizedDescription)"); return }
                guard let http = response as? HTTPURLResponse, (200...299).contains(http.statusCode), let data = data else { self?.raiseOops("服务器返回错误"); return }
                do {
                    if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
                       let codeStr: String = (json["code"] as? String) ?? ((json["code"] as? Int).map { String($0) }),
                       codeStr == "200" {
                        self?.zxNick = newNickname
                        UserDefaults.standard.set(newNickname, forKey: "memberName")
                        NotificationCenter.default.post(name: Notification.Name("UserNicknameUpdated"), object: newNickname)
                        self?.grid.reloadData()
                        self?.vx_toast("昵称修改成功")
                    } else {
                        self?.raiseOops("更新失败")
                    }
                } catch {
                    self?.raiseOops("响应解析失败")
                }
            }
        }.resume()
    }
}

// MARK: - Cell (obfuscated)
final class VXSettingCell: UITableViewCell {
    static let reuseId = "vx.setting"

    private let tLabel: UILabel = {
        let l = UILabel()
        l.font = .systemFont(ofSize: 16, weight: .medium)
        l.textColor = .gray
        l.translatesAutoresizingMaskIntoConstraints = false
        return l
    }()
    private let vLabel: UILabel = {
        let l = UILabel()
        l.font = .systemFont(ofSize: 16, weight: .medium)
        l.textColor = UIColor(hex: "#999999")
        l.translatesAutoresizingMaskIntoConstraints = false
        return l
    }()
    private let head: UIImageView = {
        let iv = UIImageView()
        iv.contentMode = .scaleAspectFill
        iv.layer.cornerRadius = 25
        iv.clipsToBounds = true
        iv.backgroundColor = UIColor(hex: "#F0F0F0")
        iv.translatesAutoresizingMaskIntoConstraints = false
        return iv
    }()
    private let arrow: UIImageView = {
        let iv = UIImageView()
        iv.image = UIImage(systemName: "chevron.right")
        iv.tintColor = UIColor(hex: "#000000")
        iv.translatesAutoresizingMaskIntoConstraints = false
        return iv
    }()
    private let line: UIView = {
        let v = UIView()
        v.backgroundColor = UIColor(hex: "#EEEEEE")
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()

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

        contentView.addSubview(tLabel)
        contentView.addSubview(vLabel)
        contentView.addSubview(arrow)
        contentView.addSubview(line)
        contentView.addSubview(head)

        NSLayoutConstraint.activate([
            tLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
            tLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),

            arrow.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
            arrow.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
            arrow.widthAnchor.constraint(equalToConstant: 12),
            arrow.heightAnchor.constraint(equalToConstant: 18),

            vLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
            vLabel.trailingAnchor.constraint(equalTo: arrow.leadingAnchor, constant: -8),

            head.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
            head.trailingAnchor.constraint(equalTo: arrow.leadingAnchor, constant: -8),
            head.widthAnchor.constraint(equalToConstant: 50),
            head.heightAnchor.constraint(equalToConstant: 50),

            line.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
            line.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
            line.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
            line.heightAnchor.constraint(equalToConstant: 0.5)
        ])
    }

    required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }

    func bind(title: String, value: String, isFirst: Bool, isLast: Bool, avatar: UIImage?, placeholder: String) {
        tLabel.text = title
        vLabel.isHidden = true
        head.isHidden = true
        if title == "头像修改" {
            head.isHidden = false
            head.image = avatar ?? UIImage(named: placeholder) ?? UIImage(named: "Home372")
        } else {
            vLabel.isHidden = false
            vLabel.text = value
        }
        line.isHidden = isLast
    }
}

// MARK: - Utils
extension VKNimbusPanelController {
    fileprivate func vx_toast(_ message: String) {
        let lab = UILabel()
        lab.text = message
        lab.font = .systemFont(ofSize: 14)
        lab.textColor = .white
        lab.backgroundColor = UIColor.black.withAlphaComponent(0.7)
        lab.textAlignment = .center
        lab.alpha = 0
        lab.layer.cornerRadius = 8
        lab.clipsToBounds = true
        lab.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(lab)
        NSLayoutConstraint.activate([
            lab.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            lab.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -100),
            lab.widthAnchor.constraint(equalToConstant: 160),
            lab.heightAnchor.constraint(equalToConstant: 40)
        ])
        UIView.animate(withDuration: 0.3, animations: { lab.alpha = 1 }) { _ in
            DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                UIView.animate(withDuration: 0.3, animations: { lab.alpha = 0 }) { _ in lab.removeFromSuperview() }
            }
        }
    }

    fileprivate func raiseOops(_ msg: String) {
        let ac = UIAlertController(title: "错误", message: msg, preferredStyle: .alert)
        ac.addAction(UIAlertAction(title: "确定", style: .default))
        present(ac, animated: true)
    }
}

// MARK: - UIImage resize
extension UIImage {
    fileprivate func x_resize(to size: CGSize) -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(size, false, scale)
        defer { UIGraphicsEndImageContext() }
        draw(in: CGRect(origin: .zero, size: size))
        return UIGraphicsGetImageFromCurrentImageContext()
    }
}

// MARK: - Debug loggers (obfuscated names)
extension VKNimbusPanelController {
    func dbg_emitRequest(name: String, request: URLRequest, body: Data?) {
        #if DEBUG
        var lines: [String] = []
        lines.append("\n====================[REQUEST: \(name)]====================")
        lines.append("URL      : \(request.url?.absoluteString ?? "-")")
        lines.append("Method   : \(request.httpMethod ?? "GET")")
        lines.append("Headers  : \(request.allHTTPHeaderFields ?? [:])")
        if let ct = request.value(forHTTPHeaderField: "Content-Type"), ct.contains("multipart/form-data"), let body = body {
            lines.append("Body     : <multipart> size=\(body.count) bytes")
        } else if let body = body, !body.isEmpty {
            if let pretty = dbg_pretty(body) { lines.append("Body(JSON): \n\(pretty)") }
            else if let s = String(data: body, encoding: .utf8) { lines.append("Body(Text): \n\(s)") }
            else { lines.append("Body(Binary) size=\(body.count) bytes") }
        } else {
            lines.append("Body     : <empty>")
        }
        lines.append("cURL     : \n\(dbg_curl(from: request, body: body))")
        lines.append("==========================================================\n")
        print(lines.joined(separator: "\n"))
        #endif
    }

    func dbg_emitResponse(name: String, data: Data?, response: URLResponse?, error: Error?) {
        #if DEBUG
        var lines: [String] = []
        lines.append("\n--------------------[RESPONSE: \(name)]--------------------")
        if let http = response as? HTTPURLResponse {
            lines.append("Status   : \(http.statusCode)")
            lines.append("URL      : \(http.url?.absoluteString ?? "-")")
            lines.append("Headers  : \(http.allHeaderFields)")
        }
        if let error = error { lines.append("Error    : \(error.localizedDescription)") }
        if let d = data, !d.isEmpty {
            if let pretty = dbg_pretty(d) { lines.append("Body(JSON): \n\(pretty)") }
            else if let s = String(data: d, encoding: .utf8) { lines.append("Body(Text): \n\(s)") }
            else { lines.append("Body(Binary) size=\(d.count) bytes") }
        } else { lines.append("Body     : <empty>") }
        lines.append("----------------------------------------------------------\n")
        print(lines.joined(separator: "\n"))
        #endif
    }

    private func dbg_pretty(_ data: Data) -> String? {
        if let obj = try? JSONSerialization.jsonObject(with: data),
           let json = try? JSONSerialization.data(withJSONObject: obj, options: [.prettyPrinted]),
           let s = String(data: json, encoding: .utf8) { return s }
        return nil
    }

    private func dbg_curl(from request: URLRequest, body: Data?) -> String {
        var parts: [String] = ["curl -i"]
        if let m = request.httpMethod { parts.append("-X \(m)") }
        if let headers = request.allHTTPHeaderFields {
            for (k, v) in headers { parts.append("-H '\(k): \(v)'") }
        }
        if let body = body, !body.isEmpty, !(request.value(forHTTPHeaderField: "Content-Type")?.contains("multipart/form-data") ?? false) {
            if let s = String(data: body, encoding: .utf8) {
                let esc = s.replacingOccurrences(of: "'", with: "'\\''")
                parts.append("--data '\(esc)'")
            }
        }
        if let u = request.url?.absoluteString { parts.append("'\(u)'") }
        return parts.joined(separator: " ")
    }
}
