import csv
import tempfile
import unittest
from pathlib import Path
from darkozagridflex import load_profile, simulate


class GridFlexTests(unittest.TestCase):
    def test_no_storage_baseline(self):
        rows = [{"load_mw": 10, "solar_mw": 4, "wind_mw": 1}]
        result = simulate(rows)
        self.assertEqual(result["energy_balance_mwh"]["unserved_load"], 5)
        self.assertEqual(result["energy_balance_mwh"]["curtailed_renewable"], 0)

    def test_battery_shifts_surplus_to_deficit(self):
        rows = [
            {"load_mw": 10, "solar_mw": 15, "wind_mw": 0},
            {"load_mw": 10, "solar_mw": 0, "wind_mw": 5},
        ]
        result = simulate(rows, battery_power_mw=5, battery_energy_mwh=5, battery_rte=1)
        self.assertAlmostEqual(result["energy_balance_mwh"]["unserved_load"], 0)
        self.assertAlmostEqual(result["storage"]["battery"]["discharge_output_mwh"], 5)
        self.assertAlmostEqual(result["service_metrics"]["load_served_fraction"], 1)

    def test_capacity_and_power_limits_are_respected(self):
        rows = [
            {"load_mw": 10, "solar_mw": 20, "wind_mw": 0},
            {"load_mw": 20, "solar_mw": 0, "wind_mw": 0},
        ]
        result = simulate(rows, battery_power_mw=5, battery_energy_mwh=3, battery_rte=1)
        self.assertAlmostEqual(result["storage"]["battery"]["charge_input_mwh"], 3)
        self.assertAlmostEqual(result["energy_balance_mwh"]["curtailed_renewable"], 7)
        self.assertAlmostEqual(result["energy_balance_mwh"]["unserved_load"], 17)

    def test_long_duration_storage_can_be_prioritized(self):
        rows = [
            {"load_mw": 10, "solar_mw": 10, "wind_mw": 10},
            {"load_mw": 10, "solar_mw": 0, "wind_mw": 0},
        ]
        result = simulate(
            rows, battery_power_mw=2, battery_energy_mwh=2, battery_rte=1,
            ldes_power_mw=8, ldes_energy_mwh=8, ldes_rte=1,
            charge_priority="ldes-first", discharge_priority="ldes-first",
        )
        self.assertAlmostEqual(result["storage"]["long_duration"]["discharge_output_mwh"], 8)
        self.assertAlmostEqual(result["storage"]["battery"]["discharge_output_mwh"], 2)
        self.assertAlmostEqual(result["energy_balance_mwh"]["unserved_load"], 0)

    def test_round_trip_losses_are_counted(self):
        rows = [
            {"load_mw": 10, "solar_mw": 15, "wind_mw": 0},
            {"load_mw": 10, "solar_mw": 0, "wind_mw": 5},
        ]
        result = simulate(rows, battery_power_mw=5, battery_energy_mwh=5, battery_rte=0.81)
        self.assertGreater(result["storage"]["battery"]["total_conversion_loss_mwh"], 0)
        self.assertGreater(result["energy_balance_mwh"]["unserved_load"], 0)

    def test_invalid_timestamp_gap_rejected(self):
        with tempfile.TemporaryDirectory() as tmp:
            path = Path(tmp) / "profile.csv"
            with path.open("w", newline="") as f:
                w = csv.writer(f)
                w.writerow(["timestamp", "load_mw", "solar_mw", "wind_mw"])
                w.writerow(["2026-01-01T00:00:00Z", 1, 0, 0])
                w.writerow(["2026-01-01T02:00:00Z", 1, 0, 0])
            with self.assertRaises(ValueError):
                load_profile(str(path))


if __name__ == "__main__":
    unittest.main()
