package auctiora;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDateTime;

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

/**
 * Test cases for ScraperDataAdapter.
 * Tests conversion from external scraper schema to monitor schema.
 */
class ScraperDataAdapterTest {

    @Test
    @DisplayName("Should extract numeric ID from text format auction ID")
    void testExtractNumericIdFromAuctionId() {
        assertEquals(39813, ScraperDataAdapter.extractNumericId("A7-39813"));
        assertEquals(12345, ScraperDataAdapter.extractNumericId("A1-12345"));
        assertEquals(0, ScraperDataAdapter.extractNumericId(null));
        assertEquals(0, ScraperDataAdapter.extractNumericId(""));
        assertEquals(0, ScraperDataAdapter.extractNumericId("ABC"));
    }

    @Test
    @DisplayName("Should extract numeric ID from text format lot ID")
    void testExtractNumericIdFromLotId() {
        // "A1-28505-5" → 285055 (concatenates all digits)
        assertEquals(285055, ScraperDataAdapter.extractNumericId("A1-28505-5"));
        assertEquals(123456, ScraperDataAdapter.extractNumericId("A7-1234-56"));
    }

    @Test
    @DisplayName("Should convert scraper auction format to AuctionInfo")
    void testFromScraperAuction() throws SQLException {
        // Mock ResultSet with scraper format data
        ResultSet rs = mock(ResultSet.class);
        when(rs.getString("auction_id")).thenReturn("A7-39813");
        when(rs.getString("title")).thenReturn("Industrial Equipment Auction");
        when(rs.getString("location")).thenReturn("Cluj-Napoca, RO");
        when(rs.getString("url")).thenReturn("https://example.com/auction/A7-39813");
        when(rs.getInt("lots_count")).thenReturn(150);
        when(rs.getString("first_lot_closing_time")).thenReturn("2025-12-15T14:30:00");

        AuctionInfo result = ScraperDataAdapter.fromScraperAuction(rs);

        assertNotNull(result);
        assertEquals(39813, result.auctionId());
        assertEquals("Industrial Equipment Auction", result.title());
        assertEquals("Cluj-Napoca, RO", result.location());
        assertEquals("Cluj-Napoca", result.city());
        assertEquals("RO", result.country());
        assertEquals("https://example.com/auction/A7-39813", result.url());
        assertEquals("A7", result.typePrefix());
        assertEquals(150, result.lotCount());
        assertNotNull(result.firstLotClosingTime());
    }

    @Test
    @DisplayName("Should handle auction with simple location without country")
    void testFromScraperAuctionSimpleLocation() throws SQLException {
        ResultSet rs = mock(ResultSet.class);
        when(rs.getString("auction_id")).thenReturn("A1-12345");
        when(rs.getString("title")).thenReturn("Test Auction");
        when(rs.getString("location")).thenReturn("Amsterdam");
        when(rs.getString("url")).thenReturn("https://example.com/auction/A1-12345");
        when(rs.getInt("lots_count")).thenReturn(50);
        when(rs.getString("first_lot_closing_time")).thenReturn(null);

        AuctionInfo result = ScraperDataAdapter.fromScraperAuction(rs);

        assertEquals("Amsterdam", result.city());
        assertEquals("", result.country());
        assertNull(result.firstLotClosingTime());
    }

    @Test
    @DisplayName("Should convert scraper lot format to Lot")
    void testFromScraperLot() throws SQLException {
        ResultSet rs = mock(ResultSet.class);
        when(rs.getString("lot_id")).thenReturn("A1-28505-5");
        when(rs.getString("auction_id")).thenReturn("A7-39813");
        when(rs.getString("title")).thenReturn("Forklift Toyota");
        when(rs.getString("description")).thenReturn("Electric forklift in good condition");
        when(rs.getString("category")).thenReturn("Machinery");
        when(rs.getString("current_bid")).thenReturn("€1250.50");
        when(rs.getString("closing_time")).thenReturn("2025-12-15T14:30:00");
        when(rs.getString("url")).thenReturn("https://example.com/lot/A1-28505-5");

        Lot result = ScraperDataAdapter.fromScraperLot(rs);

        assertNotNull(result);
        assertEquals(285055, result.lotId());
        assertEquals(39813, result.saleId());
        assertEquals("Forklift Toyota", result.title());
        assertEquals("Electric forklift in good condition", result.description());
        assertEquals("Machinery", result.category());
        assertEquals(1250.50, result.currentBid(), 0.01);
        assertEquals("EUR", result.currency());
        assertEquals("https://example.com/lot/A1-28505-5", result.url());
        assertNotNull(result.closingTime());
        assertFalse(result.closingNotified());
    }

    @Test
    @DisplayName("Should parse bid amount from various formats")
    void testParseBidAmount() throws SQLException {
        // Test €123.45 format
        ResultSet rs1 = createLotResultSet("€123.45");
        Lot lot1 = ScraperDataAdapter.fromScraperLot(rs1);
        assertEquals(123.45, lot1.currentBid(), 0.01);
        assertEquals("EUR", lot1.currency());

        // Test $50.00 format
        ResultSet rs2 = createLotResultSet("$50.00");
        Lot lot2 = ScraperDataAdapter.fromScraperLot(rs2);
        assertEquals(50.00, lot2.currentBid(), 0.01);
        assertEquals("USD", lot2.currency());

        // Test "No bids" format
        ResultSet rs3 = createLotResultSet("No bids");
        Lot lot3 = ScraperDataAdapter.fromScraperLot(rs3);
        assertEquals(0.0, lot3.currentBid(), 0.01);

        // Test plain number
        ResultSet rs4 = createLotResultSet("999.99");
        Lot lot4 = ScraperDataAdapter.fromScraperLot(rs4);
        assertEquals(999.99, lot4.currentBid(), 0.01);
    }

    @Test
    @DisplayName("Should handle missing or null fields gracefully")
    void testHandleNullFields() throws SQLException {
        ResultSet rs = mock(ResultSet.class);
        when(rs.getString("lot_id")).thenReturn("A1-12345-1");
        when(rs.getString("auction_id")).thenReturn("A7-99999");
        when(rs.getString("title")).thenReturn("Test Lot");
        when(rs.getString("description")).thenReturn(null);
        when(rs.getString("category")).thenReturn(null);
        when(rs.getString("current_bid")).thenReturn(null);
        when(rs.getString("closing_time")).thenReturn(null);
        when(rs.getString("url")).thenReturn("https://example.com/lot");

        Lot result = ScraperDataAdapter.fromScraperLot(rs);

        assertNotNull(result);
        assertEquals("", result.description());
        assertEquals("", result.category());
        assertEquals(0.0, result.currentBid());
        assertNull(result.closingTime());
    }

    @Test
    @DisplayName("Should parse various timestamp formats")
    void testTimestampParsing() throws SQLException {
        // ISO local date time
        ResultSet rs1 = mock(ResultSet.class);
        setupBasicLotMock(rs1);
        when(rs1.getString("closing_time")).thenReturn("2025-12-15T14:30:00");
        Lot lot1 = ScraperDataAdapter.fromScraperLot(rs1);
        assertNotNull(lot1.closingTime());
        assertEquals(LocalDateTime.of(2025, 12, 15, 14, 30, 0), lot1.closingTime());

        // SQL timestamp format
        ResultSet rs2 = mock(ResultSet.class);
        setupBasicLotMock(rs2);
        when(rs2.getString("closing_time")).thenReturn("2025-12-15 14:30:00");
        Lot lot2 = ScraperDataAdapter.fromScraperLot(rs2);
        assertNotNull(lot2.closingTime());
    }

    @Test
    @DisplayName("Should handle invalid timestamp gracefully")
    void testInvalidTimestamp() throws SQLException {
        ResultSet rs = mock(ResultSet.class);
        setupBasicLotMock(rs);
        when(rs.getString("closing_time")).thenReturn("invalid-date");

        Lot result = ScraperDataAdapter.fromScraperLot(rs);
        assertNull(result.closingTime());
    }

    @Test
    @DisplayName("Should extract type prefix from auction ID")
    void testTypeExtraction() throws SQLException {
        ResultSet rs1 = mock(ResultSet.class);
        when(rs1.getString("auction_id")).thenReturn("A7-39813");
        when(rs1.getString("title")).thenReturn("Test");
        when(rs1.getString("location")).thenReturn("Test, NL");
        when(rs1.getString("url")).thenReturn("http://test.com");
        when(rs1.getInt("lots_count")).thenReturn(10);
        when(rs1.getString("first_lot_closing_time")).thenReturn(null);

        AuctionInfo auction1 = ScraperDataAdapter.fromScraperAuction(rs1);
        assertEquals("A7", auction1.typePrefix());

        ResultSet rs2 = mock(ResultSet.class);
        when(rs2.getString("auction_id")).thenReturn("B1-12345");
        when(rs2.getString("title")).thenReturn("Test");
        when(rs2.getString("location")).thenReturn("Test, NL");
        when(rs2.getString("url")).thenReturn("http://test.com");
        when(rs2.getInt("lots_count")).thenReturn(10);
        when(rs2.getString("first_lot_closing_time")).thenReturn(null);

        AuctionInfo auction2 = ScraperDataAdapter.fromScraperAuction(rs2);
        assertEquals("B1", auction2.typePrefix());
    }

    @Test
    @DisplayName("Should handle GBP currency symbol")
    void testGBPCurrency() throws SQLException {
        ResultSet rs = createLotResultSet("£75.00");
        Lot lot = ScraperDataAdapter.fromScraperLot(rs);
        assertEquals(75.00, lot.currentBid(), 0.01);
        assertEquals("GBP", lot.currency());
    }

    // Helper methods

    private ResultSet createLotResultSet(String bidAmount) throws SQLException {
        ResultSet rs = mock(ResultSet.class);
        when(rs.getString("lot_id")).thenReturn("A1-12345-1");
        when(rs.getString("auction_id")).thenReturn("A7-99999");
        when(rs.getString("title")).thenReturn("Test Lot");
        when(rs.getString("description")).thenReturn("Test description");
        when(rs.getString("category")).thenReturn("Test");
        when(rs.getString("current_bid")).thenReturn(bidAmount);
        when(rs.getString("closing_time")).thenReturn("2025-12-15T14:30:00");
        when(rs.getString("url")).thenReturn("https://example.com/lot");
        return rs;
    }

    private void setupBasicLotMock(ResultSet rs) throws SQLException {
        when(rs.getString("lot_id")).thenReturn("A1-12345-1");
        when(rs.getString("auction_id")).thenReturn("A7-99999");
        when(rs.getString("title")).thenReturn("Test Lot");
        when(rs.getString("description")).thenReturn("Test");
        when(rs.getString("category")).thenReturn("Test");
        when(rs.getString("current_bid")).thenReturn("€100.00");
        when(rs.getString("url")).thenReturn("https://example.com/lot");
    }
}
