package com.ruoyi.system.omgpay.mapper;

import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

class OmgPaymentAttemptMapperContractTest {

    @Test
    void mapperAndSqlOnlyReferenceNewAttemptStorage() throws IOException {
        String xml = loadMapperXml();
        String sql = loadSql();

        assertTrue(xml.contains("FROM pos_order"));
        assertTrue(xml.contains("FOR UPDATE"));
        assertTrue(xml.contains("FROM pos_order_omg_attempt"));
        assertTrue(xml.contains("selectActiveCreatedByDdIdForUpdate"));
        assertTrue(xml.contains("selectByMerchantTradeNoForUpdate"));
        assertTrue(countOccurrences(xml, "LIMIT 1 FOR UPDATE") >= 2);
        assertFalse(xml.contains("pos_order_omg_payment"));
        assertFalse(xml.contains("pos_order_omg_refund"));
        assertTrue(sql.contains("UNIQUE KEY uk_omg_attempt_trade_no"));
        assertTrue(sql.contains("UNIQUE KEY uk_omg_attempt_active_dd"));
        assertTrue(sql.contains("UNIQUE KEY uk_omg_attempt_gateway_trade_no"));
        assertTrue(sql.contains("IF(attempt_status = 0, dd_id, NULL)"));
        assertTrue(sql.contains("hash_key_snapshot VARCHAR(64) NOT NULL"));
        assertTrue(sql.contains("hash_iv_snapshot VARCHAR(64) NOT NULL"));
        assertTrue(sql.contains("merchant_trade_no VARCHAR(20) CHARACTER SET ascii COLLATE ascii_bin"));
        assertTrue(sql.contains("merchant_id VARCHAR(10) CHARACTER SET ascii COLLATE ascii_bin"));
        assertTrue(xml.contains("attempt_status IN (0, 2, 3)"));
        assertTrue(xml.contains("attempt_status IN (0, 2)"));
        assertTrue(xml.contains("<update id=\"supersedeCreated\">"));
        assertTrue(xml.contains("WHERE id = #{id} AND attempt_status = 0"));
        assertTrue(xml.contains("UPDATE pos_order SET pay_status = 1"));
        assertTrue(xml.contains("WHERE attempt_status = 0 AND next_query_time &lt;= #{now}"));
        assertTrue(xml.contains("ORDER BY next_query_time ASC, id ASC"));
        assertTrue(xml.contains("query_count = query_count + 1"));
        assertTrue(sql.contains("idx_omg_attempt_query_due"));
        assertFalse(xml.contains("SET state ="));
        assertFalse(xml.contains("delivery_status"));
    }

    private int countOccurrences(String source, String value) {
        return (source.length() - source.replace(value, "").length()) / value.length();
    }

    private String loadMapperXml() throws IOException {
        try (InputStream inputStream = getClass().getClassLoader()
                .getResourceAsStream("mapper/omgpay/OmgPaymentAttemptMapper.xml")) {
            return new String(Objects.requireNonNull(inputStream,
                    "mapper xml not found").readAllBytes(), StandardCharsets.UTF_8);
        }
    }

    private String loadSql() throws IOException {
        for (Path candidate : sqlCandidates()) {
            if (Files.exists(candidate)) {
                return Files.readString(candidate, StandardCharsets.UTF_8);
            }
        }
        throw new IOException("updatesql/sql.md not found");
    }

    private List<Path> sqlCandidates() {
        return List.of(
                Paths.get("updatesql", "sql.md"),
                Paths.get("..", "updatesql", "sql.md")
        );
    }
}
