package com.yorvana.regression.vehicles

import android.content.ClipboardManager
import android.content.Context
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.hasClickAction
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollTo
import androidx.compose.ui.test.performTextInput
import androidx.compose.ui.test.performTextReplacement
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import com.yorvana.MainActivity
import com.yorvana.R
import com.yorvana.data.model.OdometerUnit
import com.yorvana.data.model.Vehicle
import com.yorvana.data.preferences.DebugBillingOverrideMode
import com.yorvana.testsupport.RetryRule
import com.yorvana.testsupport.app
import com.yorvana.testsupport.attachmentDir
import com.yorvana.testsupport.awaitText
import com.yorvana.testsupport.decodeJson
import com.yorvana.testsupport.installFreshVault
import com.yorvana.testsupport.killAndRelaunch
import com.yorvana.testsupport.recordFile
import com.yorvana.testsupport.resetYorvanaState
import com.yorvana.testsupport.rotateMidEdit
import com.yorvana.testsupport.seedAttachmentFile
import com.yorvana.testsupport.seedRecord
import com.yorvana.testsupport.seedVehicle
import com.yorvana.testsupport.setBillingOverride
import com.yorvana.testsupport.tiers.Regression
import com.yorvana.testsupport.vaultRoot
import com.yorvana.testsupport.vehicleDir
import com.yorvana.ui.TestTags
import kotlinx.serialization.json.Json
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import org.junit.runner.RunWith
import java.io.File

@Regression
@RunWith(AndroidJUnit4::class)
class VehicleRegressionTest {
    val retryRule = RetryRule()
    val composeRule = createAndroidComposeRule<MainActivity>()

    @get:Rule
    val ruleChain: RuleChain = RuleChain.outerRule(retryRule).around(composeRule)

    @Before
    fun setUp() {
        composeRule.resetYorvanaState()
        composeRule.installFreshVault()
    }

    @After
    fun tearDown() {
        composeRule.resetYorvanaState()
    }

    @Test
    fun rV01_addVehicleWithAllFields_persistsAcrossKillOnDisk() {
        composeRule.onNodeWithTag(TestTags.ADD_VEHICLE_FAB).performClick()
        composeRule.onNodeWithTag(TestTags.VEHICLE_NICKNAME_FIELD).performTextInput("Phase 2 Civic")
        composeRule.onNodeWithTag(TestTags.VEHICLE_MODEL_FIELD).performTextInput("Civic")
        composeRule.onNodeWithTag(TestTags.VEHICLE_YEAR_FIELD).performTextInput("2019")
        composeRule.onNodeWithTag(TestTags.VEHICLE_VIN_FIELD).performTextInput("1HGCM82633A004352")
        composeRule.onNodeWithTag(TestTags.VEHICLE_LICENSE_PLATE_FIELD).performTextInput("P2-115")
        composeRule.onNodeWithText(composeRule.activity.getString(R.string.mi)).performClick()
        composeRule.onNodeWithTag(TestTags.SAVE_BUTTON).performClick()
        composeRule.awaitText("Phase 2 Civic")

        composeRule.killAndRelaunch()

        composeRule.onNodeWithText("Phase 2 Civic").assertIsDisplayed()
        val vehicleDir =
            File(composeRule.vaultRoot(), "vehicles")
                .listFiles()
                .orEmpty()
                .single { it.name.contains("phase-2-civic") }
        val vehicle = File(vehicleDir, "vehicle.json").decodeJson<Vehicle>()
        assertThat(vehicle.nickname).isEqualTo("Phase 2 Civic")
        assertThat(vehicle.model).isEqualTo("Civic")
        assertThat(vehicle.year).isEqualTo(2019)
        assertThat(vehicle.vin).isEqualTo("1HGCM82633A004352")
        assertThat(vehicle.licensePlate).isEqualTo("P2-115")
        assertThat(vehicle.odometerUnit).isEqualTo(OdometerUnit.MI)
    }

    @Test
    fun rV02_duplicateVinGuard_blocksSecondVehicle() {
        composeRule.seedVehicle(nickname = "Original VIN", vin = "DUPLICATEVIN123456")
        composeRule.setBillingOverride(DebugBillingOverrideMode.FORCE_PREMIUM)

        composeRule.onNodeWithTag(TestTags.ADD_VEHICLE_FAB).performClick()
        composeRule.onNodeWithTag(TestTags.VEHICLE_NICKNAME_FIELD).performTextInput("Duplicate VIN")
        composeRule.onNodeWithTag(TestTags.VEHICLE_VIN_FIELD).performTextInput("duplicatevin123456")
        composeRule.onNodeWithTag(TestTags.SAVE_BUTTON).performClick()

        composeRule.awaitText(composeRule.activity.getString(R.string.error_duplicate_vin))
        assertThat(File(composeRule.vaultRoot(), "vehicles").listFiles().orEmpty()).hasLength(1)
    }

    @Test
    fun rV03_validationNegativePaths_blockSave() {
        composeRule.onNodeWithTag(TestTags.ADD_VEHICLE_FAB).performClick()
        composeRule.onNodeWithTag(TestTags.VEHICLE_NICKNAME_FIELD).performTextInput("N".repeat(51))
        composeRule.onNodeWithTag(TestTags.VEHICLE_YEAR_FIELD).performTextInput("1899")
        composeRule.onNodeWithTag(TestTags.SAVE_BUTTON).performClick()
        composeRule.awaitText(composeRule.activity.getString(R.string.error_invalid_year))
        assertThat(File(composeRule.vaultRoot(), "vehicles").listFiles().orEmpty()).isEmpty()

        composeRule.onNodeWithTag(TestTags.VEHICLE_YEAR_FIELD).performTextReplacement("2101")
        composeRule.onNodeWithTag(TestTags.SAVE_BUTTON).performClick()
        composeRule.awaitText(composeRule.activity.getString(R.string.error_invalid_year))
        assertThat(File(composeRule.vaultRoot(), "vehicles").listFiles().orEmpty()).isEmpty()
    }

    @Test
    fun rV04_editVehicle_rotationPreservesDraft() {
        composeRule.seedVehicle(nickname = "Draft Base", model = "Old")
        composeRule.onNode(hasText("Draft Base") and hasClickAction()).performClick()
        composeRule.onNodeWithTag(TestTags.MORE_OPTIONS_BUTTON).performClick()
        composeRule.onNodeWithText(composeRule.activity.getString(R.string.vehicle_edit)).performClick()
        composeRule.onNodeWithTag(TestTags.VEHICLE_MODEL_FIELD).performTextReplacement("Unsaved Model")

        composeRule.rotateMidEdit()

        composeRule.onNodeWithTag(TestTags.VEHICLE_MODEL_FIELD).performScrollTo().assertIsDisplayed()
        composeRule.onNodeWithText("Unsaved Model").assertIsDisplayed()
    }

    @Test
    fun rV05_deleteVehicleWithLargeFixture_removesVaultFiles() {
        val vehicle = composeRule.seedVehicle(nickname = "Large Delete Fixture")
        repeat(50) { index ->
            val record =
                composeRule.seedRecord(
                    vehicleId = vehicle.id,
                    id = "record-$index",
                    date = "2025-01-${((index % 28) + 1).toString().padStart(2, '0')}",
                    odometer = 10_000 + index,
                )
            if (index < 20) {
                composeRule.seedAttachmentFile(vehicle.id, record.id, "attachment-$index.txt")
            }
        }
        assertThat(composeRule.recordFile(vehicle.id, "record-49").exists()).isTrue()
        assertThat(composeRule.attachmentDir(vehicle.id, "record-19").exists()).isTrue()

        composeRule.onNodeWithText("Large Delete Fixture").performScrollTo().performClick()
        composeRule.onNodeWithTag(TestTags.MORE_OPTIONS_BUTTON).performClick()
        composeRule.onNodeWithText(composeRule.activity.getString(R.string.vehicle_delete)).performClick()
        composeRule.onNodeWithTag(TestTags.CONFIRM_DELETE_BUTTON).performClick()
        composeRule.awaitText(composeRule.activity.getString(R.string.my_garage))

        assertThat(composeRule.vehicleDir(vehicle.id).exists()).isFalse()
    }

    @Test
    fun rV06_externalVaultEdit_appearsAfterRelaunch() {
        val external =
            Vehicle(
                id = "external-car",
                nickname = "External Car",
                make = "Honda",
                model = "Fit",
                createdAt = "2026-05-23T00:00:00Z",
            )
        val dir = File(composeRule.vaultRoot(), "vehicles/external-car").also { it.mkdirs() }
        File(dir, "vehicle.json").writeText(Json.encodeToString(Vehicle.serializer(), external))

        composeRule.killAndRelaunch()

        composeRule.onNodeWithText("External Car").assertIsDisplayed()
    }

    @Test
    fun rV07_copyVin_updatesRealClipboard() {
        composeRule.seedVehicle(nickname = "VIN Clipboard", vin = "JH4KA8260MC000000")
        composeRule.onNodeWithText("VIN Clipboard").performClick()
        composeRule.onNodeWithTag(TestTags.MORE_OPTIONS_BUTTON).performClick()
        composeRule.onNodeWithText(composeRule.activity.getString(R.string.vehicle_edit)).performClick()
        composeRule.onNodeWithTag(TestTags.VEHICLE_VIN_COPY_BUTTON).performClick()

        val clipboard = composeRule.app().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
        var clipboardText: String? = null
        val start = System.currentTimeMillis()
        while (System.currentTimeMillis() - start < 5000) {
            clipboardText =
                clipboard
                    .primaryClip
                    ?.getItemAt(0)
                    ?.coerceToText(composeRule.activity)
                    ?.toString()
            if (clipboardText == "JH4KA8260MC000000") {
                break
            }
            Thread.sleep(100)
        }
        assertThat(clipboardText).isEqualTo("JH4KA8260MC000000")
    }
}
