from fastapi import status
from fastapi.testclient import TestClient
from sqlalchemy.orm import Session
from datetime import date

from tests.helpers import generators


# Helper Function
def _login_user(db: Session, client: TestClient):
    user, password = generators.create_user(db)
    login_rsp = generators.login(db, user.username, password)
    return user, login_rsp["access_token"], login_rsp["refresh_token"]


# --- Test CRUD Operations ---


def test_create_todo(client: TestClient, db: Session):
    user, access_token, refresh_token = _login_user(db, client)
    today_date = date.today()
    # Format the date string to match the expected response format "YYYY-MM-DDTHH:MM:SS"
    todo_data = {
        "task": "Test TODO",
        "date": f"{today_date.isoformat()}T00:00:00",
        "remind": True,
    }

    response = client.post(
        "/api/todos/",
        headers={"Authorization": f"Bearer {access_token}"},
        cookies={"refresh_token": refresh_token},
        json=todo_data,
    )

    assert response.status_code == status.HTTP_201_CREATED
    data = response.json()
    assert data["task"] == todo_data["task"]
    assert data["date"] == todo_data["date"]
    assert data["remind"] == todo_data["remind"]
    assert data["complete"] is False  # Default
    assert "id" in data
    assert data["owner_id"] == user.id


def test_read_todos(client: TestClient, db: Session):
    user, access_token, refresh_token = _login_user(db, client)
    # Create some todos for the user
    client.post(
        "/api/todos/",
        headers={"Authorization": f"Bearer {access_token}"},
        cookies={"refresh_token": refresh_token},
        json={"task": "Todo 1"},
    )
    client.post(
        "/api/todos/",
        headers={"Authorization": f"Bearer {access_token}"},
        cookies={"refresh_token": refresh_token},
        json={"task": "Todo 2"},
    )

    # Create a todo for another user
    other_user, other_password = generators.create_user(db)
    other_login_rsp = generators.login(db, other_user.username, other_password)
    other_access_token = other_login_rsp["access_token"]
    other_refresh_token = other_login_rsp["refresh_token"]
    client.post(
        "/api/todos/",
        headers={"Authorization": f"Bearer {other_access_token}"},
        cookies={"refresh_token": other_refresh_token},
        json={"task": "Other User Todo"},
    )

    response = client.get(
        "/api/todos/",
        headers={"Authorization": f"Bearer {access_token}"},
        cookies={"refresh_token": refresh_token},
    )

    assert response.status_code == status.HTTP_200_OK
    data = response.json()
    assert len(data) == 2  # Should only get todos for the logged-in user
    assert data[0]["task"] == "Todo 1"
    assert data[1]["task"] == "Todo 2"


def test_read_single_todo(client: TestClient, db: Session):
    user, access_token, refresh_token = _login_user(db, client)
    create_response = client.post(
        "/api/todos/",
        headers={"Authorization": f"Bearer {access_token}"},
        cookies={"refresh_token": refresh_token},
        json={"task": "Specific Todo"},
    )
    todo_id = create_response.json()["id"]

    response = client.get(
        f"/api/todos/{todo_id}",
        headers={"Authorization": f"Bearer {access_token}"},
        cookies={"refresh_token": refresh_token},
    )

    assert response.status_code == status.HTTP_200_OK
    data = response.json()
    assert data["id"] == todo_id
    assert data["task"] == "Specific Todo"
    assert data["owner_id"] == user.id


def test_read_single_todo_not_found(client: TestClient, db: Session):
    user, access_token, refresh_token = _login_user(db, client)
    response = client.get(
        "/api/todos/9999",  # Non-existent ID
        headers={"Authorization": f"Bearer {access_token}"},
        cookies={"refresh_token": refresh_token},
    )
    assert response.status_code == status.HTTP_404_NOT_FOUND


def test_read_single_todo_forbidden(client: TestClient, db: Session):
    user, access_token, refresh_token = _login_user(db, client)

    # Create a todo for another user
    other_user, other_password = generators.create_user(db)
    other_login_rsp = generators.login(db, other_user.username, other_password)
    other_access_token = other_login_rsp["access_token"]
    other_refresh_token = other_login_rsp["refresh_token"]
    other_create_response = client.post(
        "/api/todos/",
        headers={"Authorization": f"Bearer {other_access_token}"},
        cookies={"refresh_token": other_refresh_token},
        json={"task": "Other User Todo"},
    )
    other_todo_id = other_create_response.json()["id"]

    # Try to access the other user's todo
    response = client.get(
        f"/api/todos/{other_todo_id}",
        headers={
            "Authorization": f"Bearer {access_token}"
        },  # Using the first user's token
        cookies={"refresh_token": refresh_token},
    )
    assert (
        response.status_code == status.HTTP_404_NOT_FOUND
    )  # Service raises 404 if not found for *this* user


def test_update_todo(client: TestClient, db: Session):
    user, access_token, refresh_token = _login_user(db, client)
    create_response = client.post(
        "/api/todos/",
        headers={"Authorization": f"Bearer {access_token}"},
        cookies={"refresh_token": refresh_token},
        json={"task": "Update Me"},
    )
    todo_id = create_response.json()["id"]

    update_data = {"task": "Updated Task", "complete": True}
    response = client.put(
        f"/api/todos/{todo_id}",
        headers={"Authorization": f"Bearer {access_token}"},
        cookies={"refresh_token": refresh_token},
        json=update_data,
    )

    assert response.status_code == status.HTTP_200_OK
    data = response.json()
    assert data["id"] == todo_id
    assert data["task"] == update_data["task"]
    assert data["complete"] == update_data["complete"]
    assert data["owner_id"] == user.id

    # Verify update by reading again
    get_response = client.get(
        f"/api/todos/{todo_id}",
        headers={"Authorization": f"Bearer {access_token}"},
        cookies={"refresh_token": refresh_token},
    )
    assert get_response.json()["task"] == update_data["task"]
    assert get_response.json()["complete"] == update_data["complete"]


def test_update_todo_not_found(client: TestClient, db: Session):
    user, access_token, refresh_token = _login_user(db, client)
    update_data = {"task": "Updated Task", "complete": True}
    response = client.put(
        "/api/todos/9999",  # Non-existent ID
        headers={"Authorization": f"Bearer {access_token}"},
        cookies={"refresh_token": refresh_token},
        json=update_data,
    )
    assert response.status_code == status.HTTP_404_NOT_FOUND


def test_delete_todo(client: TestClient, db: Session):
    user, access_token, refresh_token = _login_user(db, client)
    create_response = client.post(
        "/api/todos/",
        headers={"Authorization": f"Bearer {access_token}"},
        cookies={"refresh_token": refresh_token},
        json={"task": "Delete Me"},
    )
    todo_id = create_response.json()["id"]

    response = client.delete(
        f"/api/todos/{todo_id}",
        headers={"Authorization": f"Bearer {access_token}"},
        cookies={"refresh_token": refresh_token},
    )

    assert response.status_code == status.HTTP_200_OK  # Delete returns the deleted item
    assert response.json()["id"] == todo_id

    # Verify deletion by trying to read
    get_response = client.get(
        f"/api/todos/{todo_id}",
        headers={"Authorization": f"Bearer {access_token}"},
        cookies={"refresh_token": refresh_token},
    )
    assert get_response.status_code == status.HTTP_404_NOT_FOUND


def test_delete_todo_not_found(client: TestClient, db: Session):
    user, access_token, refresh_token = _login_user(db, client)
    response = client.delete(
        "/api/todos/9999",  # Non-existent ID
        headers={"Authorization": f"Bearer {access_token}"},
        cookies={"refresh_token": refresh_token},
    )
    assert response.status_code == status.HTTP_404_NOT_FOUND


# --- Test Authentication/Authorization ---


def test_create_todo_unauthorized(client: TestClient):
    response = client.post("/api/todos/", json={"task": "No Auth"})
    assert response.status_code == status.HTTP_401_UNAUTHORIZED


def test_read_todos_unauthorized(client: TestClient):
    response = client.get("/api/todos/")
    assert response.status_code == status.HTTP_401_UNAUTHORIZED
