package com.lingniu.ingest.identity;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.lingniu.ingest.api.ProtocolId;
import com.lingniu.ingest.identity.config.VehicleIdentityProperties;
import org.junit.jupiter.api.Test;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;

class MySqlVehicleIdentityServiceTest {

    @Test
    void upsertsRegistrationBindingAndResolvesByExternalIdentifiers() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource(
                "jdbc:h2:mem:mysql-identity;MODE=MySQL;DATABASE_TO_UPPER=false;DB_CLOSE_DELAY=-1", "sa", "");
        VehicleIdentityProperties.Mysql mysql = new VehicleIdentityProperties.Mysql();
        mysql.setTableName("vehicle_identity_bindings");
        MySqlVehicleIdentityService service = new MySqlVehicleIdentityService(
                dataSource,
                mysql,
                new ObjectMapper());

        service.bind(new VehicleIdentityBinding(
                ProtocolId.JT808,
                "JT808AABBCCDDEE1",
                "13079963289",
                "9963289",
                "沪A00113F",
                Map.of(
                        "province", "31",
                        "city", "113",
                        "maker", "70112",
                        "deviceType", "SEG-9888G",
                        "plateColor", "2")));

        assertThat(service.resolve(new VehicleIdentityLookup(ProtocolId.JT808, "", "13079963289", "", "")))
                .extracting(VehicleIdentity::vin, VehicleIdentity::resolved, VehicleIdentity::source)
                .containsExactly("JT808AABBCCDDEE1", true, VehicleIdentitySource.BOUND_PHONE);
        assertThat(service.resolve(new VehicleIdentityLookup(ProtocolId.JT808, "", "", "9963289", "")))
                .extracting(VehicleIdentity::vin, VehicleIdentity::resolved, VehicleIdentity::source)
                .containsExactly("JT808AABBCCDDEE1", true, VehicleIdentitySource.BOUND_DEVICE_ID);
        assertThat(service.resolve(new VehicleIdentityLookup(ProtocolId.JT808, "", "", "", "沪A00113F")))
                .extracting(VehicleIdentity::vin, VehicleIdentity::resolved, VehicleIdentity::source)
                .containsExactly("JT808AABBCCDDEE1", true, VehicleIdentitySource.BOUND_PLATE);

        JdbcTemplate jdbc = new JdbcTemplate(dataSource);
        Map<String, Object> row = jdbc.queryForMap("""
                SELECT vin, phone, device_id, plate, province, city, maker, device_type, plate_color
                FROM vehicle_identity_bindings
                WHERE protocol = 'JT808' AND phone = '13079963289'
                """);
        assertThat(row)
                .containsEntry("vin", "JT808AABBCCDDEE1")
                .containsEntry("phone", "13079963289")
                .containsEntry("device_id", "9963289")
                .containsEntry("plate", "沪A00113F")
                .containsEntry("province", "31")
                .containsEntry("city", "113")
                .containsEntry("maker", "70112")
                .containsEntry("device_type", "SEG-9888G")
                .containsEntry("plate_color", "2");
    }

    @Test
    void initializesMissingColumnsOnExistingLegacyTable() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource(
                "jdbc:h2:mem:mysql-identity-legacy;MODE=MySQL;DATABASE_TO_UPPER=false;DB_CLOSE_DELAY=-1", "sa", "");
        JdbcTemplate jdbc = new JdbcTemplate(dataSource);
        jdbc.execute("""
                CREATE TABLE vehicle_identity_binding (
                    id BIGINT AUTO_INCREMENT PRIMARY KEY,
                    protocol VARCHAR(32) NOT NULL,
                    vin VARCHAR(32) NOT NULL,
                    legacy_key VARCHAR(64) NOT NULL DEFAULT ''
                )
                """);
        VehicleIdentityProperties.Mysql mysql = new VehicleIdentityProperties.Mysql();
        mysql.setTableName("vehicle_identity_binding");

        MySqlVehicleIdentityService service = new MySqlVehicleIdentityService(
                dataSource,
                mysql,
                new ObjectMapper());
        service.bind(new VehicleIdentityBinding(
                ProtocolId.JT808,
                "JT808LEGACY00001",
                "13079963289",
                "9963289",
                "沪A00113F",
                Map.of("maker", "70112", "deviceType", "SEG-9888G")));

        Map<String, Object> row = jdbc.queryForMap("""
                SELECT vin, maker, device_type, metadata_json
                FROM vehicle_identity_binding
                WHERE protocol = 'JT808' AND phone = '13079963289'
                """);
        assertThat(row)
                .containsEntry("vin", "JT808LEGACY00001")
                .containsEntry("maker", "70112")
                .containsEntry("device_type", "SEG-9888G");
        assertThat((String) row.get("metadata_json")).contains("SEG-9888G");
    }

    @Test
    void upsertsByCompositeProtocolPhonePlatePrimaryKey() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource(
                "jdbc:h2:mem:mysql-identity-composite;MODE=MySQL;DATABASE_TO_UPPER=false;DB_CLOSE_DELAY=-1", "sa", "");
        JdbcTemplate jdbc = new JdbcTemplate(dataSource);
        jdbc.execute("""
                CREATE TABLE vehicle_identity_binding (
                    protocol VARCHAR(32) NOT NULL DEFAULT 'ALL',
                    phone VARCHAR(128) NOT NULL DEFAULT '',
                    plate VARCHAR(128) NOT NULL DEFAULT '',
                    vin VARCHAR(64) NOT NULL,
                    province VARCHAR(32) NOT NULL DEFAULT '',
                    city VARCHAR(32) NOT NULL DEFAULT '',
                    maker VARCHAR(64) NOT NULL DEFAULT '',
                    device_type VARCHAR(128) NOT NULL DEFAULT '',
                    plate_color VARCHAR(32) NOT NULL DEFAULT '',
                    metadata_json TEXT,
                    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                    device_id VARCHAR(64) NOT NULL DEFAULT '',
                    PRIMARY KEY (protocol, phone, plate)
                )
                """);
        VehicleIdentityProperties.Mysql mysql = new VehicleIdentityProperties.Mysql();
        mysql.setTableName("vehicle_identity_binding");
        MySqlVehicleIdentityService service = new MySqlVehicleIdentityService(
                dataSource,
                mysql,
                new ObjectMapper());
        service.bind(new VehicleIdentityBinding(ProtocolId.JT808,
                "VIN-A", "13079963289", "DEV-A", "沪A00113F"));

        service.bind(new VehicleIdentityBinding(ProtocolId.JT808,
                "VIN-B", "13079963289", "DEV-B", "沪A00114F"));
        service.bind(new VehicleIdentityBinding(ProtocolId.JT808,
                "VIN-B2", "13079963289", "DEV-B2", "沪A00114F"));

        Integer rows = jdbc.queryForObject("""
                SELECT COUNT(*)
                FROM vehicle_identity_binding
                WHERE protocol = 'JT808' AND phone = '13079963289'
                """, Integer.class);
        assertThat(rows).isEqualTo(2);
        assertThat(jdbc.queryForObject("""
                SELECT vin
                FROM vehicle_identity_binding
                WHERE protocol = 'JT808' AND phone = '13079963289' AND plate = '沪A00114F'
                """, String.class)).isEqualTo("VIN-B2");
    }
}
