package ru.forbion.f7cloud.core.network

import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneId
import org.junit.Test

/**
 * Тесты парсера ICS. Фиксируют баги из аудита (2026-07-07): зоны/TZID, ATTENDEE, unescape.
 * Даты с суффиксом Z дают абсолютный Instant → не зависят от таймзоны машины.
 */
class CalendarIcsTest {

    // --- parseIcsInstant: зоны ---

    @Test fun `Z-суффикс парсится как UTC`() {
        // Баг: раньше Z срезался и время трактовалось как локальное.
        val expected = Instant.parse("2026-07-07T10:00:00Z")
        assertEquals(expected, CalendarIcs.parseIcsInstant("20260707T100000Z"))
    }

    @Test fun `floating без Z — локальная зона`() {
        val expected = LocalDateTime.of(2026, 7, 7, 10, 0, 0)
            .atZone(ZoneId.systemDefault()).toInstant()
        assertEquals(expected, CalendarIcs.parseIcsInstant("20260707T100000"))
    }

    @Test fun `TZID учитывается`() {
        // 12:00 в Москве (UTC+3) == 09:00 UTC
        val expected = Instant.parse("2026-07-07T09:00:00Z")
        assertEquals(expected, CalendarIcs.parseIcsInstant("20260707T120000", "Europe/Moscow"))
    }

    @Test fun `VALUE=DATE — начало дня`() {
        val expected = java.time.LocalDate.of(2026, 7, 7)
            .atStartOfDay(ZoneId.systemDefault()).toInstant()
        assertEquals(expected, CalendarIcs.parseIcsInstant("20260707"))
    }

    // --- ATTENDEE: несколько участников с параметрами ---

    @Test fun `несколько ATTENDEE не схлопываются и хранят CN-PARTSTAT`() {
        val ics = """
            BEGIN:VCALENDAR
            BEGIN:VEVENT
            UID:test-1
            DTSTART:20260707T100000Z
            SUMMARY:Встреча
            ATTENDEE;CN=Иван Петров;PARTSTAT=ACCEPTED;ROLE=REQ-PARTICIPANT:mailto:ivan@f7.ru
            ATTENDEE;CN=Мария Сидорова;PARTSTAT=DECLINED;ROLE=OPT-PARTICIPANT:mailto:maria@f7.ru
            END:VEVENT
            END:VCALENDAR
        """.trimIndent()
        val event = CalendarIcs.parseSingle(ics)!!
        assertEquals(2, event.attendees.size)
        val ivan = event.attendees.first { it.email == "ivan@f7.ru" }
        assertEquals("Иван Петров", ivan.displayName)
        assertEquals("ACCEPTED", ivan.partStat)
        val maria = event.attendees.first { it.email == "maria@f7.ru" }
        assertEquals("Мария Сидорова", maria.displayName)
        assertEquals("DECLINED", maria.partStat)
    }

    // --- escape/unescape: круговой round-trip со спецсимволами ---

    @Test fun `summary со спецсимволами переживает build и parse`() {
        val summary = """Смета: 50%, скидка; путь C:\tmp\файл и
            перенос""".trimIndent()
        val data = CalendarEventData(
            uid = "rt-1",
            summary = summary,
            startEpochMilli = Instant.parse("2026-07-07T10:00:00Z").toEpochMilli(),
            endEpochMilli = Instant.parse("2026-07-07T11:00:00Z").toEpochMilli(),
        )
        val parsed = CalendarIcs.parseSingle(CalendarIcs.build(data))!!
        assertEquals(summary, parsed.summary)
    }

    @Test fun `unescape не превращает экранированный бэкслеш-n в перенос строки`() {
        // literal "\n" (бэкслеш+n) в тексте: escape → "\\n", unescape должен вернуть "\n" (2 символа),
        // а не перевод строки. Проверяем через round-trip.
        val summary = """путь\name"""
        val data = CalendarEventData(
            uid = "rt-2",
            summary = summary,
            startEpochMilli = 0L,
            endEpochMilli = 3600_000L,
        )
        val parsed = CalendarIcs.parseSingle(CalendarIcs.build(data))!!
        assertEquals(summary, parsed.summary)
        assertTrue("не должно быть переноса строки", !parsed.summary.contains('\n'))
    }

    // --- согласованность двух парсеров ---

    @Test fun `CalDavClient и CalendarIcs парсят одинаково`() {
        val values = listOf("20260707T100000Z", "20260707", "20251231T235900Z")
        for (v in values) {
            assertEquals("рассинхрон на $v", CalendarIcs.parseIcsInstant(v), CalDavClient.parseIcsInstant(v))
        }
    }
}
