"""
Acceptance tests for Battery Tracker.

Each test gets a fresh in-memory SQLite database via the fixtures in conftest.py.
The `seeded_client` fixture pre-populates:
  Devices:   Device A (2 slots), Device B (1 slot)
  Batteries: BrandX 001 (id=1, available), BrandY 001 (id=2, available),
             BrandX 002 (id=3, retired)
"""

import pytest


# ------------------------------------------------------------------ #
# Helpers
# ------------------------------------------------------------------ #

def get_location(resp):
    """Return the redirect Location header (without host)."""
    loc = resp.headers.get("Location", "")
    if loc.startswith("http"):
        from urllib.parse import urlparse
        loc = urlparse(loc).path
    return loc


def follow(client, resp):
    """Follow a single redirect."""
    return client.get(get_location(resp))


# ------------------------------------------------------------------ #
# Dashboard
# ------------------------------------------------------------------ #

def test_dashboard_loads(seeded_client):
    resp = seeded_client.get("/")
    assert resp.status_code == 200
    assert b"BrandX 001" in resp.data
    assert b"BrandY 001" in resp.data
    assert b"BrandX 002" in resp.data


# ------------------------------------------------------------------ #
# Battery add — label preview data
# ------------------------------------------------------------------ #

def test_battery_add_get_renders(client):
    resp = client.get("/battery/add")
    assert resp.status_code == 200
    assert b"brandCounts" in resp.data


def test_battery_add_brand_counts_reflects_existing(seeded_client):
    # seeded_client has BrandX x2, BrandY x1
    resp = seeded_client.get("/battery/add")
    assert resp.status_code == 200
    assert b'"BrandX": 2' in resp.data or b'"BrandX":2' in resp.data
    assert b'"BrandY": 1' in resp.data or b'"BrandY":1' in resp.data


# ------------------------------------------------------------------ #
# Battery — bulk add
# ------------------------------------------------------------------ #

def test_bulk_add_batteries(client):
    resp = client.post("/battery/add",
                       data={"brand": "Eneloop", "count": "3"},
                       follow_redirects=True)
    assert resp.status_code == 200
    assert b"Eneloop 001" in resp.data
    assert b"Eneloop 002" in resp.data
    assert b"Eneloop 003" in resp.data


def test_bulk_add_autogenerates_sequential_labels(client):
    client.post("/battery/add", data={"brand": "Eneloop", "count": "2"})
    resp = client.post("/battery/add", data={"brand": "Eneloop", "count": "2"},
                       follow_redirects=True)
    assert b"Eneloop 003" in resp.data
    assert b"Eneloop 004" in resp.data


def test_bulk_add_missing_brand(client):
    resp = client.post("/battery/add", data={"brand": "", "count": "1"})
    assert resp.status_code == 400
    assert b"required" in resp.data.lower()


# ------------------------------------------------------------------ #
# Battery — detail
# ------------------------------------------------------------------ #

def test_battery_detail(seeded_client):
    resp = seeded_client.get("/battery/1")
    assert resp.status_code == 200
    assert b"BrandX 001" in resp.data
    assert b"BrandX" in resp.data


def test_battery_detail_not_found(client):
    resp = client.get("/battery/9999")
    assert resp.status_code == 404


# ------------------------------------------------------------------ #
# Battery — edit notes
# ------------------------------------------------------------------ #

def test_edit_notes(seeded_client):
    seeded_client.post("/battery/1/edit-details", data={"notes": "test note here"})
    resp = seeded_client.get("/battery/1")
    assert b"test note here" in resp.data


# ------------------------------------------------------------------ #
# Battery — assign (per-battery, kept for special cases)
# ------------------------------------------------------------------ #

def test_assign_battery(seeded_client):
    resp = seeded_client.post("/battery/1/assign",
                              data={"device_id": "1"},
                              follow_redirects=True)
    assert resp.status_code == 200
    assert b"Device A" in resp.data


def test_assign_retired_battery_blocked(seeded_client):
    # id=3 is retired
    resp = seeded_client.post("/battery/3/assign",
                              data={"device_id": "1"},
                              follow_redirects=True)
    assert resp.status_code == 200
    assert b"retired" in resp.data.lower()


def test_assign_over_capacity_blocked(seeded_client):
    # Fill Device B (1 slot) with id=1
    seeded_client.post("/battery/1/assign", data={"device_id": "2"})
    # Try to assign id=2 to the same full device
    resp = seeded_client.post("/battery/2/assign",
                              data={"device_id": "2"},
                              follow_redirects=True)
    assert resp.status_code == 200
    assert b"full" in resp.data.lower()


def test_brand_mix_warning(seeded_client):
    # Assign BrandX 001 (id=1) to Device A
    seeded_client.post("/battery/1/assign", data={"device_id": "1"})
    # Assign BrandY 001 (id=2) to same Device A → brand mix warning, but succeeds
    resp = seeded_client.post("/battery/2/assign",
                              data={"device_id": "1"},
                              follow_redirects=True)
    assert resp.status_code == 200
    assert b"mixing" in resp.data.lower() or b"mix" in resp.data.lower() or b"brand" in resp.data.lower()


# ------------------------------------------------------------------ #
# Battery — unassign
# ------------------------------------------------------------------ #

def test_unassign_battery(seeded_client):
    seeded_client.post("/battery/1/assign", data={"device_id": "1"})
    resp = seeded_client.post("/battery/1/unassign", follow_redirects=True)
    assert resp.status_code == 200
    resp2 = seeded_client.get("/battery/1")
    assert b"available" in resp2.data.lower()


# ------------------------------------------------------------------ #
# Battery — retire
# ------------------------------------------------------------------ #

def test_retire_battery(seeded_client):
    resp = seeded_client.post("/battery/1/retire", follow_redirects=True)
    assert resp.status_code == 200
    resp2 = seeded_client.get("/battery/1")
    assert b"retired" in resp2.data.lower()


def test_retire_clears_device(seeded_client):
    seeded_client.post("/battery/1/assign", data={"device_id": "1"})
    seeded_client.post("/battery/1/retire")
    resp = seeded_client.get("/battery/1")
    assert b"None" in resp.data or b"retired" in resp.data.lower()


def test_retire_already_retired(seeded_client):
    # id=3 is already retired by fixture
    resp = seeded_client.post("/battery/3/retire", follow_redirects=True)
    assert resp.status_code == 200
    assert b"already retired" in resp.data.lower()


# ------------------------------------------------------------------ #
# Battery — delete
# ------------------------------------------------------------------ #

def test_delete_battery_confirmation_page(seeded_client):
    resp = seeded_client.get("/battery/1/delete")
    assert resp.status_code == 200
    assert b"Confirm Delete" in resp.data
    assert b"BrandX 001" in resp.data


def test_delete_battery(seeded_client):
    seeded_client.post("/battery/1/delete")
    resp = seeded_client.get("/battery/1")
    assert resp.status_code == 404


# ------------------------------------------------------------------ #
# Battery — bulk actions
# ------------------------------------------------------------------ #

def test_bulk_retire(client):
    client.post("/battery/add", data={"brand": "TestBrand", "count": "2"})
    resp = client.post("/battery/bulk-action",
                       data={"battery_ids": ["1", "2"], "action": "retire"},
                       follow_redirects=True)
    assert resp.status_code == 200
    assert b"retired" in client.get("/battery/1").data.lower()
    assert b"retired" in client.get("/battery/2").data.lower()


def test_bulk_delete(client):
    client.post("/battery/add", data={"brand": "TestBrand", "count": "2"})
    resp = client.post("/battery/bulk-action",
                       data={"battery_ids": ["1", "2"], "action": "delete"},
                       follow_redirects=True)
    assert resp.status_code == 200
    assert client.get("/battery/1").status_code == 404
    assert client.get("/battery/2").status_code == 404


def test_bulk_unassign(client):
    client.post("/device/add", data={"name": "Gadget", "battery_slots": "2"})
    client.post("/battery/add", data={"brand": "TestBrand", "count": "2"})
    client.post("/device/1/install", data={"brand[]": "TestBrand", "qty[]": "2"})
    resp = client.post("/battery/bulk-action",
                       data={"battery_ids": ["1", "2"], "action": "unassign"},
                       follow_redirects=True)
    assert resp.status_code == 200
    assert b"available" in client.get("/battery/1").data.lower()


def test_bulk_set_brand(client):
    client.post("/battery/add", data={"brand": "OldBrand", "count": "2"})
    resp = client.post("/battery/bulk-action",
                       data={"battery_ids": ["1", "2"], "action": "set_brand",
                             "new_brand": "NewBrand"},
                       follow_redirects=True)
    assert resp.status_code == 200
    assert b"NewBrand" in client.get("/battery/1").data


def test_bulk_action_no_selection(client):
    client.post("/battery/add", data={"brand": "TestBrand", "count": "1"})
    resp = client.post("/battery/bulk-action",
                       data={"action": "retire"},
                       follow_redirects=True)
    assert resp.status_code == 200
    assert b"No batteries selected" in resp.data


# ------------------------------------------------------------------ #
# Device — install batteries
# ------------------------------------------------------------------ #

def test_device_install_autoselects(client):
    client.post("/device/add", data={"name": "Gadget", "battery_slots": "2"})
    client.post("/battery/add", data={"brand": "Eneloop", "count": "3"})
    resp = client.post("/device/1/install",
                       data={"brand[]": "Eneloop", "qty[]": "2"},
                       follow_redirects=True)
    assert resp.status_code == 200
    # Device detail should show 2 installed batteries
    assert b"Eneloop" in resp.data
    assert b"2 / 2" in resp.data


def test_device_install_mixed_brands(client):
    client.post("/device/add", data={"name": "Remote", "battery_slots": "4"})
    client.post("/battery/add", data={"brand": "Eneloop", "count": "2"})
    client.post("/battery/add", data={"brand": "Energizer", "count": "2"})
    resp = client.post("/device/1/install",
                       data={"brand[]": ["Eneloop", "Energizer"], "qty[]": ["2", "2"]},
                       follow_redirects=True)
    assert resp.status_code == 200
    assert b"Eneloop" in resp.data
    assert b"Energizer" in resp.data


def test_device_install_insufficient_batteries(client):
    client.post("/device/add", data={"name": "Gadget", "battery_slots": "4"})
    client.post("/battery/add", data={"brand": "Eneloop", "count": "1"})
    resp = client.post("/device/1/install",
                       data={"brand[]": "Eneloop", "qty[]": "2"},
                       follow_redirects=True)
    assert resp.status_code == 200
    assert b"available" in resp.data.lower()


def test_device_install_over_capacity(client):
    client.post("/device/add", data={"name": "Gadget", "battery_slots": "1"})
    client.post("/battery/add", data={"brand": "Eneloop", "count": "3"})
    resp = client.post("/device/1/install",
                       data={"brand[]": "Eneloop", "qty[]": "3"},
                       follow_redirects=True)
    assert resp.status_code == 200
    assert b"slot" in resp.data.lower()


def test_bulk_install_device(client):
    """Select multiple available batteries and install them into a device."""
    client.post("/device/add", data={"name": "Box", "battery_slots": "3"})
    client.post("/battery/add", data={"brand": "Eneloop", "count": "2"})
    resp = client.post("/battery/bulk-action",
                       data={"battery_ids": ["1", "2"], "action": "install_device",
                             "device_id": "1"},
                       follow_redirects=True)
    assert resp.status_code == 200
    assert b"Installed 2" in resp.data


def test_bulk_install_device_over_capacity(client):
    """Bulk install is blocked when device lacks free slots."""
    client.post("/device/add", data={"name": "Box", "battery_slots": "1"})
    client.post("/battery/add", data={"brand": "Eneloop", "count": "2"})
    resp = client.post("/battery/bulk-action",
                       data={"battery_ids": ["1", "2"], "action": "install_device",
                             "device_id": "1"},
                       follow_redirects=True)
    assert resp.status_code == 200
    assert b"slot" in resp.data.lower()


def test_bulk_install_device_moves_installed_battery(client):
    """Bulk install moves a battery already installed in another device."""
    client.post("/device/add", data={"name": "Box A", "battery_slots": "2"})
    client.post("/device/add", data={"name": "Box B", "battery_slots": "2"})
    client.post("/battery/add", data={"brand": "Eneloop", "count": "1"})
    client.post("/battery/1/assign", data={"device_id": "1"})
    resp = client.post("/battery/bulk-action",
                       data={"battery_ids": ["1"], "action": "install_device",
                             "device_id": "2"},
                       follow_redirects=True)
    assert resp.status_code == 200
    assert b"Installed 1" in resp.data
    assert b"Box B" in client.get("/battery/1").data


# ------------------------------------------------------------------ #
# Dashboard — quick-assign
# ------------------------------------------------------------------ #

def test_dashboard_quick_assign(client):
    """Quick-assign from dashboard (battery_assign POST) succeeds."""
    client.post("/device/add", data={"name": "Box", "battery_slots": "2"})
    client.post("/battery/add", data={"brand": "Eneloop", "count": "1"})
    resp = client.post("/battery/1/assign", data={"device_id": "1"},
                       follow_redirects=True)
    assert resp.status_code == 200
    assert b"Box" in resp.data


def test_dashboard_quick_assign_full_device_blocked(client):
    """Quick-assign from dashboard to a full device is blocked."""
    client.post("/device/add", data={"name": "Box", "battery_slots": "1"})
    client.post("/battery/add", data={"brand": "Eneloop", "count": "2"})
    client.post("/battery/1/assign", data={"device_id": "1"})  # fills the slot
    resp = client.post("/battery/2/assign", data={"device_id": "1"},
                       follow_redirects=True)
    assert resp.status_code == 200
    assert b"full" in resp.data.lower()


# ------------------------------------------------------------------ #
# Device — install-one (specific battery)
# ------------------------------------------------------------------ #

def test_install_one_specific_battery(client):
    """device_install_one installs a chosen battery."""
    client.post("/device/add", data={"name": "Box", "battery_slots": "2"})
    client.post("/battery/add", data={"brand": "Eneloop", "count": "1"})
    resp = client.post("/device/1/install-one", data={"battery_id": "1"},
                       follow_redirects=True)
    assert resp.status_code == 200
    assert b"Eneloop 001" in resp.data


def test_install_one_over_capacity_blocked(client):
    """device_install_one to a full device is blocked."""
    client.post("/device/add", data={"name": "Box", "battery_slots": "1"})
    client.post("/battery/add", data={"brand": "Eneloop", "count": "2"})
    client.post("/device/1/install-one", data={"battery_id": "1"})  # fills slot
    resp = client.post("/device/1/install-one", data={"battery_id": "2"},
                       follow_redirects=True)
    assert resp.status_code == 200
    assert b"full" in resp.data.lower()


# ------------------------------------------------------------------ #
# Device — list
# ------------------------------------------------------------------ #

def test_device_list(seeded_client):
    resp = seeded_client.get("/device/")
    assert resp.status_code == 200
    assert b"Device A" in resp.data
    assert b"Device B" in resp.data


# ------------------------------------------------------------------ #
# Device — add
# ------------------------------------------------------------------ #

def test_add_device(client):
    resp = client.post("/device/add",
                       data={"name": "My Gadget", "battery_slots": "3"},
                       follow_redirects=True)
    assert resp.status_code == 200
    assert b"My Gadget" in resp.data


def test_add_device_duplicate_name(seeded_client):
    resp = seeded_client.post("/device/add",
                              data={"name": "Device A", "battery_slots": "2"})
    assert resp.status_code == 400
    assert b"already exists" in resp.data


def test_add_device_missing_name(client):
    resp = client.post("/device/add", data={"name": "", "battery_slots": "1"})
    assert resp.status_code == 400


# ------------------------------------------------------------------ #
# Device — detail
# ------------------------------------------------------------------ #

def test_device_detail(seeded_client):
    resp = seeded_client.get("/device/1")
    assert resp.status_code == 200
    assert b"Device A" in resp.data
    assert b"2" in resp.data  # battery_slots


def test_device_detail_not_found(client):
    resp = client.get("/device/9999")
    assert resp.status_code == 404


# ------------------------------------------------------------------ #
# Device — delete
# ------------------------------------------------------------------ #

def test_delete_device_frees_batteries(seeded_client):
    seeded_client.post("/battery/1/assign", data={"device_id": "1"})
    resp = seeded_client.post("/device/1/delete", follow_redirects=True)
    assert resp.status_code == 200
    resp2 = seeded_client.get("/battery/1")
    assert b"available" in resp2.data.lower()


def test_delete_device_removed(seeded_client):
    seeded_client.post("/device/1/delete")
    resp = seeded_client.get("/device/1")
    assert resp.status_code == 404


# ------------------------------------------------------------------ #
# Full round-trip
# ------------------------------------------------------------------ #

def test_add_device_with_type(client):
    resp = client.post("/device/add",
                       data={"name": "TV Remote", "battery_slots": "2", "device_type": "Remote Control"},
                       follow_redirects=True)
    assert resp.status_code == 200
    assert b"TV Remote" in resp.data


def test_device_detail_shows_type(client):
    client.post("/device/add", data={"name": "Torch", "battery_slots": "1", "device_type": "Flashlight"})
    resp = client.get("/device/1")
    assert resp.status_code == 200
    assert b"Flashlight" in resp.data


def test_edit_device_type(client):
    client.post("/device/add", data={"name": "Torch", "battery_slots": "1"})
    resp = client.post("/device/1/edit",
                       data={"name": "Torch", "battery_slots": "1", "device_type": "Flashlight"},
                       follow_redirects=True)
    assert resp.status_code == 200
    assert b"Flashlight" in resp.data


def test_add_capacity_test(client):
    client.post("/battery/add", data={"brand": "Eneloop", "count": "1"})
    resp = client.post("/battery/1/capacity-test/add",
                       data={"tested_capacity_mah": "1900", "tested_date": "2026-01-01"},
                       follow_redirects=True)
    assert resp.status_code == 200
    assert b"1900" in resp.data


def test_capacity_test_syncs_battery(client):
    """Adding a test updates battery.tested_capacity_mah."""
    client.post("/battery/add", data={"brand": "Eneloop", "count": "1"})
    client.post("/battery/1/capacity-test/add",
                data={"tested_capacity_mah": "1800", "tested_date": "2026-01-01"})
    resp = client.get("/battery/1")
    assert b"1800" in resp.data


def test_delete_capacity_test(client):
    client.post("/battery/add", data={"brand": "Eneloop", "count": "1"})
    client.post("/battery/1/capacity-test/add",
                data={"tested_capacity_mah": "1900", "tested_date": "2026-01-01"})
    resp = client.post("/battery/1/capacity-test/1/delete", follow_redirects=True)
    assert resp.status_code == 200
    assert b"No test records" in resp.data


def test_add_install_delete_battery(client):
    client.post("/device/add", data={"name": "Gadget", "battery_slots": "1"})
    client.post("/battery/add", data={"brand": "AcmeBrand", "count": "1"})
    resp = client.post("/device/1/install",
                       data={"brand[]": "AcmeBrand", "qty[]": "1"},
                       follow_redirects=True)
    assert b"Gadget" in resp.data
    client.post("/battery/1/delete")
    assert client.get("/battery/1").status_code == 404
