import com.alibaba.fastjson2.JSONObject;
import com.kym.huapiaoer.HuapiaoerClient;
import com.kym.huapiaoer.HuapiaoerConfig;
import com.kym.huapiaoer.HuapiaoerException;
import com.kym.huapiaoer.enums.InvoiceQueryType;
import com.kym.huapiaoer.enums.InvoiceType;
import com.kym.huapiaoer.enums.PayType;
import com.kym.huapiaoer.model.request.InvoiceQrcodeRequest;
import com.kym.huapiaoer.model.request.OpenBlueInvoiceRequest;
import com.kym.huapiaoer.model.response.InvoiceInfo;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import static org.junit.jupiter.api.Assertions.*;

/**
 * 航信电子发票 API 测试。
 * 直接使用 HuapiaoerClient，无需启动 Spring 容器。
 *
 * 运行前请确认 application.yml 中 huapiaoer 相关配置已正确填写：
 *   huapiaoer.app-secret
 *   huapiaoer.nsrsbh
 *   huapiaoer.b2cshopid
 */
public class HuapiaoerApiTest {

    private static final String APP_SECRET = "xK9mQ2pR5nT7vL4cW6fY8jH1aB3sD0gZ";
    private static final String NSRSBH = "91440300MA5HJNDG14";
    private static final String B2C_SHOP_ID = "91440300MA5HJNDG14";
    private static final String BASE_URL = "https://erp.huapiaoer.com";

    private static HuapiaoerClient client;

    @BeforeAll
    public static void setUp() {
        HuapiaoerConfig config = HuapiaoerConfig.builder()
                .baseUrl(BASE_URL)
                .appSecret(APP_SECRET)
                .build();
        client = new HuapiaoerClient(config);
    }

    /**
     * 测试 1：查询开票人列表（只读，安全）。
     * 验证 appsecret 和 nsrsbh 是否有效。
     */
    @Test
    public void testGetDrawerIdList() {
        System.out.println("=== 测试：查询开票人列表 ===");
        var drawers = client.getDrawerIdList(NSRSBH);
        assertNotNull(drawers);
        System.out.println("开票人数量: " + drawers.size());
        drawers.forEach(d -> System.out.println("  issuerid=" + d.getIssuerid() + ", name=" + d.getName()));
    }

    /**
     * 测试 2：企业搜索（只读，安全）。
     * 通过企业名称模糊查询税号等信息。
     */
    @Test
    public void testSearchEnterprise() {
        System.out.println("=== 测试：企业搜索 ===");
        var enterprises = client.searchEnterprise("快与慢");
        assertNotNull(enterprises);
        System.out.println("搜索结果数量: " + enterprises.size());
        enterprises.forEach(e -> System.out.println("  name=" + e.getEnterpriseName() + ", taxpayerNum=" + e.getTaxpayerNum()));
    }

    /**
     * 测试 3：生成开票二维码（只读，安全）。
     * 生成二维码后用户可扫码自助开票，不会直接开票。
     */
    @Test
    public void testGetInvoiceQrcode() {
        System.out.println("=== 测试：生成开票二维码 ===");
        String orderId = "TEST-" + UUID.randomUUID().toString().substring(0, 8);
        var head = new InvoiceQrcodeRequest.HeadInfo()
                .setOrderId(orderId)
                .setB2cshopid(B2C_SHOP_ID)
                .setInvoiceTypeCode(InvoiceType.DIGITAL_NORMAL.getCode());
        var request = new InvoiceQrcodeRequest()
                .setHead(head);
        var resp = client.getInvoiceQrcode(request);
        System.out.println("code=" + resp.getCode() + ", data=" + resp.getData());
        assertNotNull(resp);
    }

    /**
     * 测试 4：开具蓝字发票（会产生真实发票，慎用）。
     * 默认跳过，需要时手动去掉 @Test 注释。
     *
     * 注意：此操作会在税局系统生成一张真实的电子发票！
     * 建议使用极小金额（如 0.01 元）进行测试。
     */
    /**
     * 测试 4：开具蓝字发票 + 轮询查询结果（完整开票流程）。
     * 注意：此操作会在税局系统生成一张真实的电子发票（金额 0.01 元）！
     *
     * 税编说明：航信要求使用非汇总项的 19 位税收分类编码。
     * 如果开票失败，请根据后台错误信息调整 taxcode。
     */
    @Test
    public void testOpenBlueInvoice() {
        System.out.println("=== 测试：开具蓝字发票 ===");
        String orderId = "TEST-BLUE-" + UUID.randomUUID().toString().substring(0, 8);
        System.out.println("出库单号: " + orderId);

        // 先获取开票人 ID
        var drawers = client.getDrawerIdList(NSRSBH);
        assertFalse(drawers.isEmpty(), "至少需要一个开票人");
        String issuerId = drawers.get(0).getIssuerid();
        System.out.println("开票人 issuerid: " + issuerId);

        // 税编：使用叶子节点编码（非汇总项）
        //   "1100101020200000000" - 供电*电费 (13%)  -- 已验证可用
        //   "3040101020100000000" - 专业技术服务 (6%)

        var detail1 = new OpenBlueInvoiceRequest.DetailInfo()
                .setGoodsname("充电电费")
                .setPrice(0.01)
                .setSl(1.0)
                .setTaxcode("1100101020200000000")
                .setTax("0.13");

        var detail2 = new OpenBlueInvoiceRequest.DetailInfo()
                .setGoodsname("充电服务费")
                .setPrice(0.01)
                .setSl(1.0)
                .setTaxcode("1100101020200000000")
                .setTax("0.13");

        var head = new OpenBlueInvoiceRequest.HeadInfo()
                .setOrderId(orderId)
                .setB2cshopid(B2C_SHOP_ID)
                .setKpje("0.02")
                .setInvoicetitle("测试个人")
                .setInvoiceTypeCode(InvoiceType.DIGITAL_NORMAL.getCode());

        var request = new OpenBlueInvoiceRequest()
                .setIssuerid(issuerId)
                .setHead(head)
                .setDetails(List.of(detail1, detail2))
                .setTradeinfo(new OpenBlueInvoiceRequest.TradeInfo()
                        .setPayType(PayType.WECHAT_PAY.getCode())
                        .setOutTradeNo(orderId));

        // 构建完整请求 JSON（模拟 SDK 的 buildBody）
        JSONObject body = (JSONObject) com.alibaba.fastjson2.JSON.toJSON(request);
        body.put("appsecret", APP_SECRET);
        String reqJson = body.toJSONString();

        // Step 1: 开票
        var resp = client.openBlueInvoice(request);
        System.out.println("开票响应: code=" + resp.getCode() + ", orderId=" + resp.getOrderId());
        if (!resp.isSuccess()) {
            System.out.println("【请求体】\n" + reqJson);
            // 用原始 HTTP 获取完整响应
            try {
                var httpClient = new OkHttpClient.Builder()
                        .connectTimeout(10, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).build();
                var httpReq = new okhttp3.Request.Builder()
                        .url(BASE_URL + "/ewy_weberp/api/huapiaoer/outopenapis/openBlueInvoice")
                        .post(RequestBody.create(reqJson, MediaType.parse("application/json")))
                        .build();
                var httpResp = httpClient.newCall(httpReq).execute();
                String respBody = httpResp.body() != null ? httpResp.body().string() : "";
                System.out.println("【原始响应】HTTP " + httpResp.code() + "\n" + respBody);
            } catch (Exception ex) {
                System.out.println("【原始请求异常】" + ex.getMessage());
            }
        }
        assertTrue(resp.isSuccess(), "开票请求应返回 code=200, 实际 code=" + resp.getCode() + ", data=" + resp.getData());
        System.out.println("开票请求成功！orderId=" + resp.getOrderId());

        // Step 2: 轮询查询开票结果
        System.out.println("开始轮询查询开票结果（每 5 秒一次，最多 2 分钟）...");
        InvoiceInfo invoiceInfo = null;
        for (int i = 0; i < 24; i++) {
            try { Thread.sleep(5000); } catch (InterruptedException ignored) {}
            try {
                invoiceInfo = client.getInvoice(orderId, InvoiceQueryType.BLUE.getCode());
                System.out.println("  [" + ((i + 1) * 5) + "s] 查询成功！");
                break;
            } catch (HuapiaoerException e) {
                System.out.println("  [" + ((i + 1) * 5) + "s] 尚未完成: " + e.getMessage());
            }
        }

        assertNotNull(invoiceInfo, "应在超时前查询到发票结果");
        System.out.println("=== 开票结果 ===");
        System.out.println("发票号码: " + invoiceInfo.getInvoicenumber());
        System.out.println("发票代码: " + invoiceInfo.getInvoicecode());
        System.out.println("开票日期: " + invoiceInfo.getTicketDate());
        System.out.println("价税合计: " + invoiceInfo.getTicketTotalAmountHasTax());
        System.out.println("PDF 地址: " + invoiceInfo.getInvoiceurl());
        System.out.println("OFD 地址: " + invoiceInfo.getOfdurl());
        System.out.println("XML 地址: " + invoiceInfo.getXmlurl());
    }

    /**
     * 测试 5：查询发票结果（只读，安全）。
     * 用失败码测试错误处理是否正确。
     */
    @Test
    public void testQueryNonExistentInvoice() {
        System.out.println("=== 测试：查询不存在的发票 ===");
        try {
            client.getInvoice("NOT-EXIST-ORDER-001", InvoiceQueryType.BLUE.getCode());
            System.out.println("意外的成功返回");
        } catch (HuapiaoerException e) {
            System.out.println("预期错误 - code=" + e.getCode() + ", msg=" + e.getMessage());
            assertNotNull(e.getCode());
        }
    }
}
