import os
import json
import re
import unittest
from datetime import datetime, timedelta, timezone
from uuid import UUID, uuid4

from fastapi.testclient import TestClient
from sqlalchemy import create_engine, delete
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import StaticPool

# Ensure settings can be initialized in test environments
os.environ.setdefault("DATABASE_URL", "sqlite+pysqlite:///:memory:")
os.environ.setdefault("REDIS_URL", "redis://localhost:6379/0")
os.environ.setdefault("S3_ENDPOINT", "http://localhost:9000")
os.environ.setdefault("S3_ACCESS_KEY", "test")
os.environ.setdefault("S3_SECRET_KEY", "test")
os.environ.setdefault("S3_BUCKET", "test")

from app.core.config import settings
from app.core.security import create_jwt, verify_password
from app.db.session import get_db
from app.main import app
from app.models.admin_user import AdminUser
from app.models.admin_user_topic import AdminUserTopic
from app.models.attachment import Attachment
from app.models.audit_log import AuditLog
from app.models.client import Client
from app.models.form_field import FormField
from app.models.message import Message
from app.models.notification import Notification
from app.models.invoice import Invoice
from app.models.table_availability import TableAvailability
from app.models.quote import Quote
from app.models.request import Request
from app.models.status import Status
from app.models.status_group import StatusGroup
from app.models.status_history import StatusHistory
from app.models.topic_data_template import TopicDataTemplate
from app.models.topic import Topic
from app.models.topic_required_field import TopicRequiredField
from app.models.request_data_requirement import RequestDataRequirement
from app.models.request_service_request import RequestServiceRequest
from app.models.topic_status_transition import TopicStatusTransition


class AdminUniversalCrudBase(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.engine = create_engine(
            "sqlite+pysqlite:///:memory:",
            connect_args={"check_same_thread": False},
            poolclass=StaticPool,
        )
        cls.SessionLocal = sessionmaker(bind=cls.engine, autocommit=False, autoflush=False)
        AdminUser.__table__.create(bind=cls.engine)
        Client.__table__.create(bind=cls.engine)
        Quote.__table__.create(bind=cls.engine)
        FormField.__table__.create(bind=cls.engine)
        Request.__table__.create(bind=cls.engine)
        StatusGroup.__table__.create(bind=cls.engine)
        Status.__table__.create(bind=cls.engine)
        Message.__table__.create(bind=cls.engine)
        Attachment.__table__.create(bind=cls.engine)
        StatusHistory.__table__.create(bind=cls.engine)
        Topic.__table__.create(bind=cls.engine)
        TopicRequiredField.__table__.create(bind=cls.engine)
        TopicDataTemplate.__table__.create(bind=cls.engine)
        RequestDataRequirement.__table__.create(bind=cls.engine)
        RequestServiceRequest.__table__.create(bind=cls.engine)
        TopicStatusTransition.__table__.create(bind=cls.engine)
        AdminUserTopic.__table__.create(bind=cls.engine)
        Notification.__table__.create(bind=cls.engine)
        Invoice.__table__.create(bind=cls.engine)
        TableAvailability.__table__.create(bind=cls.engine)
        AuditLog.__table__.create(bind=cls.engine)

    @classmethod
    def tearDownClass(cls):
        AuditLog.__table__.drop(bind=cls.engine)
        Notification.__table__.drop(bind=cls.engine)
        Invoice.__table__.drop(bind=cls.engine)
        TableAvailability.__table__.drop(bind=cls.engine)
        AdminUserTopic.__table__.drop(bind=cls.engine)
        RequestDataRequirement.__table__.drop(bind=cls.engine)
        RequestServiceRequest.__table__.drop(bind=cls.engine)
        TopicDataTemplate.__table__.drop(bind=cls.engine)
        TopicRequiredField.__table__.drop(bind=cls.engine)
        TopicStatusTransition.__table__.drop(bind=cls.engine)
        Topic.__table__.drop(bind=cls.engine)
        StatusHistory.__table__.drop(bind=cls.engine)
        Attachment.__table__.drop(bind=cls.engine)
        Message.__table__.drop(bind=cls.engine)
        Status.__table__.drop(bind=cls.engine)
        StatusGroup.__table__.drop(bind=cls.engine)
        Request.__table__.drop(bind=cls.engine)
        FormField.__table__.drop(bind=cls.engine)
        Quote.__table__.drop(bind=cls.engine)
        Client.__table__.drop(bind=cls.engine)
        AdminUser.__table__.drop(bind=cls.engine)
        cls.engine.dispose()

    def setUp(self):
        with self.SessionLocal() as db:
            db.execute(delete(AuditLog))
            db.execute(delete(StatusHistory))
            db.execute(delete(Attachment))
            db.execute(delete(Message))
            db.execute(delete(Request))
            db.execute(delete(StatusGroup))
            db.execute(delete(Client))
            db.execute(delete(Status))
            db.execute(delete(FormField))
            db.execute(delete(Topic))
            db.execute(delete(TopicRequiredField))
            db.execute(delete(TopicDataTemplate))
            db.execute(delete(RequestDataRequirement))
            db.execute(delete(RequestServiceRequest))
            db.execute(delete(TopicStatusTransition))
            db.execute(delete(AdminUserTopic))
            db.execute(delete(Notification))
            db.execute(delete(Invoice))
            db.execute(delete(TableAvailability))
            db.execute(delete(Quote))
            db.execute(delete(AdminUser))
            db.commit()

        def override_get_db():
            db = self.SessionLocal()
            try:
                yield db
            finally:
                db.close()

        app.dependency_overrides[get_db] = override_get_db
        self.client = TestClient(app)

    def tearDown(self):
        self.client.close()
        app.dependency_overrides.clear()

    @staticmethod
    def _auth_headers(role: str, email: str | None = None, sub: str | None = None) -> dict[str, str]:
        token = create_jwt(
            {"sub": str(sub or uuid4()), "email": email or f"{role.lower()}@example.com", "role": role},
            settings.ADMIN_JWT_SECRET,
            timedelta(minutes=30),
        )
        return {"Authorization": f"Bearer {token}"}
