"""Test fixtures for preloop.models."""

import os  # Added import

import pytest
from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker


@pytest.fixture(scope="session")
def db_engine():
    """Create a test database engine. Uses DATABASE_URL if set, otherwise raises error."""
    # Read database URL from environment variable
    db_url = os.environ.get("DATABASE_URL")
    if not db_url:
        raise ValueError(
            "DATABASE_URL environment variable not set. "
            "This is required for tests needing PostgreSQL with PGVector."
        )

    # Use PostgreSQL for tests requiring it (like embedding tests)
    # Assumes the database specified by DATABASE_URL exists and has PGVector enabled.
    engine = create_engine(db_url)

    with engine.connect() as connection:
        connection.execute(text("CREATE EXTENSION IF NOT EXISTS vector"))
        connection.commit()

    # NOTE: We do NOT drop and recreate tables here!
    # This would affect the production database if DATABASE_URL is set incorrectly.
    # Tests use transaction rollbacks for isolation, so existing tables are fine.
    # If you need to reset the schema, do it manually outside of tests.

    yield engine

    # NOTE: DO NOT drop tables in teardown!
    # Tests use transactions that are rolled back, so no cleanup needed.
    # Dropping tables would affect the actual database if DATABASE_URL is set incorrectly.


@pytest.fixture(scope="function")
def db_session(db_engine):
    """Create a new database session for a test."""
    # Create a new session for each test
    connection = db_engine.connect()
    transaction = connection.begin()

    # Create session factory bound to the connection
    session_factory = sessionmaker(autocommit=False, autoflush=False, bind=connection)
    session = session_factory()

    yield session

    # Roll back the transaction after the test completes
    session.close()
    transaction.rollback()
    connection.close()


@pytest.fixture
def create_account(db_session):
    """Create a test account (organization).

    Note: In the multi-user architecture, Account represents an organization.
    To create a user, use create_user() fixture instead.
    """
    import uuid

    from preloop.models.crud import crud_account

    def _create_account(organization_name=None, **kwargs):
        # Generate unique organization name if not provided
        unique_id = str(uuid.uuid4())[:8]
        organization_name = organization_name or f"Test Organization {unique_id}"

        account_data = {
            "organization_name": organization_name,
            "is_active": True,
            **kwargs,
        }
        return crud_account.create(db_session, obj_in=account_data)

    return _create_account


@pytest.fixture
def create_user(db_session, create_account):
    """Create a test user within an account."""
    import uuid

    from preloop.models.crud import crud_user

    def _create_user(username=None, email=None, account=None, **kwargs):
        # Create account if not provided
        if account is None:
            account = create_account()

        # Generate unique username and email if not provided
        unique_id = str(uuid.uuid4())[:8]
        username = username or f"testuser_{unique_id}"
        email = email or f"test_{unique_id}@example.com"

        user_data = {
            "username": username,
            "email": email,
            "account_id": account.id,
            "hashed_password": "hashed_password",
            "is_active": True,
            **kwargs,
        }
        return crud_user.create(db_session, obj_in=user_data)

    return _create_user


@pytest.fixture
def create_tracker(db_session, create_account):
    """Create a test tracker."""
    from preloop.models.crud import crud_tracker

    def _create_tracker(
        account=None, tracker_type="github", name="Test Tracker", **kwargs
    ):
        if account is None:
            account = create_account()

        tracker_data = {
            "name": name,
            "tracker_type": tracker_type,
            "account_id": account.id,
            "api_key": "test_api_key",
            "url": "https://example.com",
            "is_active": True,
            "jira_webhook_id": None,
            "jira_webhook_secret": None,
            **kwargs,
        }
        # Ensure that if tracker_type is jira, url is provided.
        if tracker_data["tracker_type"] == "jira" and not tracker_data.get("url"):
            tracker_data["url"] = "https://jira.example.com"

        return crud_tracker.create(db_session, obj_in=tracker_data)

    return _create_tracker


@pytest.fixture
def create_organization(db_session, create_tracker):
    """Create a test organization."""
    import uuid

    from preloop.models.crud import crud_organization

    def _create_organization(tracker=None, name=None, identifier=None, **kwargs):
        # Generate unique values if not provided
        unique_id = str(uuid.uuid4())[:8]
        name = name or f"Test Organization {unique_id}"
        identifier = identifier or f"test-org-{unique_id}"

        if tracker is None:
            tracker = create_tracker()

        org_data = {
            "name": name,
            "identifier": identifier,
            "tracker_id": tracker.id,
            "is_active": True,
            **kwargs,
        }
        return crud_organization.create(db_session, obj_in=org_data)

    return _create_organization


@pytest.fixture
def create_project(db_session, create_organization):
    """Create a test project."""
    from preloop.models.crud import crud_project

    def _create_project(
        name="Test Project", identifier="test-project", slug=None, **kwargs
    ):
        # Create organization first if not provided
        organization = kwargs.pop("organization", create_organization())

        project_data = {
            "name": name,
            "identifier": identifier,
            "slug": slug,  # Add slug field
            "organization_id": organization.id,
            "is_active": True,
            **kwargs,
        }
        return crud_project.create(db_session, obj_in=project_data)

    return _create_project


@pytest.fixture
def create_issue(db_session, create_project, create_tracker):
    """Create a test issue."""
    import uuid  # Import uuid
    from preloop.models.crud import crud_issue

    def _create_issue(title="Test Issue", description="Test description", **kwargs):
        # Create project and tracker first if not provided
        project = kwargs.pop("project", create_project())
        tracker = kwargs.pop("tracker", create_tracker())

        # Generate default unique values if not provided
        default_key = f"TEST-KEY-{uuid.uuid4().hex[:6]}"
        default_external_id = f"EXT-{uuid.uuid4().hex[:8]}"

        issue_data = {
            "title": title,
            "description": description,
            "status": "open",
            "issue_type": "task",
            "project_id": project.id,
            "tracker_id": tracker.id,
            "key": kwargs.pop("key", default_key),  # Use default or kwargs
            "external_id": kwargs.pop(
                "external_id", default_external_id
            ),  # Use default or kwargs
            **kwargs,  # Include remaining kwargs
        }
        return crud_issue.create(db_session, obj_in=issue_data)

    return _create_issue


@pytest.fixture
def create_comment(db_session, create_issue, create_user):
    """Create a test comment.
    Handles 'issue_id' or 'issue' object passed in kwargs,
    or creates a default issue.
    Handles 'author' (as username string or User object) passed in kwargs,
    or creates a default author user.
    """
    from preloop.models.crud import crud_comment
    from preloop.models.models import User, Issue

    def _create_comment(body="Test comment body", type="issue", **kwargs):
        current_issue: "Issue"

        if "issue_id" in kwargs:
            issue_id = str(kwargs.pop("issue_id"))
            current_issue = db_session.query(Issue).filter(Issue.id == issue_id).one()
        elif "issue" in kwargs:
            current_issue = kwargs.pop("issue")
        else:
            current_issue = create_issue()

        author_obj = None
        if "author" in kwargs:
            author_arg = kwargs.pop("author")
            if isinstance(author_arg, str):  # Username provided
                author_obj = (
                    db_session.query(User).filter(User.username == author_arg).first()
                )
                if not author_obj:
                    raise ValueError(
                        f"Test setup error: author with username '{author_arg}' not found."
                    )
            elif isinstance(author_arg, User):  # User object provided
                author_obj = author_arg

        if not author_obj:
            author_obj = create_user()

        comment_data = {
            "body": body,
            "type": type,
            "issue_id": str(current_issue.id),
            "tracker_id": str(current_issue.tracker_id),
            "author": author_obj.username,
            "external_id": kwargs.pop("external_id", "default-external-id"),
            **kwargs,
        }

        return crud_comment.create(db_session, obj_in=comment_data)

    return _create_comment


@pytest.fixture
def create_embedding_model(db_session):
    """Create a test embedding model."""
    from preloop.models.crud import crud_embedding_model

    def _create_embedding_model(
        name="test-embedding",
        provider="openai",
        version="text-embedding-ada-002",
        dimensions=1536,
        **kwargs,
    ):
        model_data = {
            "name": name,
            "provider": provider,
            "version": version,
            "dimensions": dimensions,
            "is_active": True,
            "meta_data": {},
            **kwargs,
        }
        return crud_embedding_model.create(db_session, obj_in=model_data)

    return _create_embedding_model
