package com.dangerousthings.aliro;

import com.licel.jcardsim.smartcardio.CardSimulator;
import javacard.framework.AID;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
 * Tests covering the Aliro Expedited Phase SELECT handler, per spec &sect;10.2.1.
 */
class AliroAppletTest {

    private CardSimulator sim;

    @BeforeEach
    void setUp() {
        sim = new CardSimulator();
        AID expeditedAid = new AID(AliroAids.EXPEDITED, (short) 0, (byte) AliroAids.EXPEDITED.length);
        sim.installApplet(expeditedAid, AliroApplet.class);
    }

    private ResponseAPDU selectExpedited() {
        CommandAPDU select = new CommandAPDU(0x00, 0xA4, 0x04, 0x00, AliroAids.EXPEDITED, 256);
        return sim.transmitCommand(select);
    }

    @Test
    void selectExpeditedAidReturns9000() {
        assertEquals(0x9000, selectExpedited().getSW(),
            "SELECT expedited AID must return success SW");
    }

    @Test
    void selectExpeditedReturnsNonEmptyFci() {
        byte[] data = selectExpedited().getData();
        assertTrue(data.length > 0, "SELECT response data must be non-empty (FCI template)");
    }

    @Test
    void selectExpeditedResponseIsFciTemplate() {
        byte[] data = selectExpedited().getData();
        assertEquals(0x6F, data[0] & 0xFF, "top-level tag must be 0x6F (FCI template)");
    }

    @Test
    void fciContainsAidTagMatchingExpeditedAid() {
        byte[] data = selectExpedited().getData();
        byte[] fci = TlvUtil.findTopLevel(data, 0x6F);
        assertNotNull(fci, "must contain FCI template");

        byte[] aid = TlvUtil.findTopLevel(fci, 0x84);
        assertNotNull(aid, "FCI must contain AID tag 0x84");
        assertArrayEquals(AliroAids.EXPEDITED, aid, "AID tag value must equal expedited AID");
    }

    @Test
    void fciContainsProprietaryInfoTagA5() {
        byte[] data = selectExpedited().getData();
        byte[] fci = TlvUtil.findTopLevel(data, 0x6F);
        byte[] propInfo = TlvUtil.findTopLevel(fci, 0xA5);
        assertNotNull(propInfo, "FCI must contain Proprietary Information tag 0xA5");
    }

    @Test
    void proprietaryInfoContainsCsaApplicationType() {
        byte[] data = selectExpedited().getData();
        byte[] fci = TlvUtil.findTopLevel(data, 0x6F);
        byte[] propInfo = TlvUtil.findTopLevel(fci, 0xA5);

        byte[] type = TlvUtil.findTopLevel(propInfo, 0x80);
        assertNotNull(type, "Proprietary Information must contain Type tag 0x80");
        assertArrayEquals(new byte[] { 0x00, 0x00 }, type, "Type must be 0x0000 (CSA application)");
    }

    @Test
    void proprietaryInfoContainsExpeditedProtocolVersion() {
        byte[] data = selectExpedited().getData();
        byte[] fci = TlvUtil.findTopLevel(data, 0x6F);
        byte[] propInfo = TlvUtil.findTopLevel(fci, 0xA5);

        byte[] versions = TlvUtil.findTopLevel(propInfo, 0x5C);
        assertNotNull(versions, "expedited SELECT must include protocol versions tag 0x5C");
        assertTrue(versions.length >= 2 && versions.length % 2 == 0,
            "protocol versions must be a non-empty list of 2-byte entries");
        assertEquals(0x01, versions[0] & 0xFF, "expected first protocol version high byte 0x01");
        assertEquals(0x00, versions[1] & 0xFF, "expected first protocol version low byte 0x00");
    }

    @Test
    void auth1ExchangeInsRejectedNowThatStepUpAppletOwnsIt() {
        // StepUpApplet (ACCE5502) owns INS_EXCHANGE (0xC9); AliroApplet must
        // NOT also answer 0xC9 — a spec-conformant reader routes EXCHANGE to
        // ACCE5502 after the §10.2 step-up AID SELECT. AliroApplet receiving
        // 0xC9 is a reader bug or a stale flow, and must be rejected with
        // SW_INS_NOT_SUPPORTED so it can never accidentally interact with
        // expedited session state.
        selectExpedited();
        CommandAPDU exchange = new CommandAPDU(0x80, 0xC9, 0x00, 0x00);
        ResponseAPDU resp = sim.transmitCommand(exchange);
        assertEquals(0x6D00, resp.getSW(),
            "AliroApplet must reject INS_EXCHANGE (0xC9) — StepUpApplet (ACCE5502) owns it now");
    }
}
