"""
GraphQL API Implementation
Provides GraphQL alternative to REST endpoints
"""
from typing import List, Optional, Any
from datetime import datetime
import strawberry
from strawberry.fastapi import GraphQLRouter
from strawberry.types import Info
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.database import AsyncSessionLocal
from app.models.client import Client, ClientAPIKey
from app.models.conversation import Conversation, Message
from app.models.subscription import Plan, Subscription
from app.models.billing import Invoice, Payment, UsageLog
from app.models.webhook import ClientWebhook


# ============== Scalar Types ==============

@strawberry.type
class DateTimeScalar:
    value: datetime


# ============== GraphQL Types ==============

@strawberry.type
class ClientType:
    id: int
    company_name: str
    company_email: str
    company_phone: Optional[str]
    company_website: Optional[str]
    industry: Optional[str]
    company_size: Optional[str]
    contact_name: str
    contact_email: str
    status: str
    chatbot_name: str
    chatbot_welcome_message: str
    monthly_message_limit: int
    daily_message_limit: int
    created_at: datetime


@strawberry.type
class ConversationType:
    id: int
    client_id: int
    session_id: str
    visitor_id: Optional[str]
    visitor_name: Optional[str]
    visitor_email: Optional[str]
    channel: str
    status: str
    message_count: int
    started_at: datetime
    ended_at: Optional[datetime]


@strawberry.type
class MessageType:
    id: int
    conversation_id: int
    role: str
    content: str
    tokens_used: int
    created_at: datetime


@strawberry.type
class PlanType:
    id: int
    name: str
    description: Optional[str]
    price_monthly: float
    price_yearly: float
    monthly_messages: int
    max_chatbots: int
    features: List[str]
    is_active: bool


@strawberry.type
class SubscriptionType:
    id: int
    client_id: int
    plan_id: int
    status: str
    billing_cycle: str
    current_period_start: datetime
    current_period_end: datetime
    created_at: datetime


@strawberry.type
class InvoiceType:
    id: int
    client_id: int
    invoice_number: str
    amount: float
    tax_amount: float
    total_amount: float
    currency: str
    status: str
    due_date: datetime
    paid_at: Optional[datetime]
    created_at: datetime


@strawberry.type
class WebhookType:
    id: int
    name: str
    url: str
    events: List[str]
    is_active: bool
    total_deliveries: int
    successful_deliveries: int
    failed_deliveries: int
    created_at: datetime


@strawberry.type
class UsageStatsType:
    total_messages: int
    total_conversations: int
    total_tokens: int
    period_start: datetime
    period_end: datetime


@strawberry.type
class PaginationInfo:
    total: int
    page: int
    per_page: int
    total_pages: int
    has_next: bool
    has_prev: bool


@strawberry.type
class ConversationConnection:
    items: List[ConversationType]
    pagination: PaginationInfo


@strawberry.type
class MessageConnection:
    items: List[MessageType]
    pagination: PaginationInfo


@strawberry.type
class InvoiceConnection:
    items: List[InvoiceType]
    pagination: PaginationInfo


# ============== Input Types ==============

@strawberry.input
class ConversationFilterInput:
    status: Optional[str] = None
    channel: Optional[str] = None
    visitor_email: Optional[str] = None
    start_date: Optional[datetime] = None
    end_date: Optional[datetime] = None


@strawberry.input
class PaginationInput:
    page: int = 1
    per_page: int = 20


# ============== Query Resolvers ==============

async def get_db_session() -> AsyncSession:
    async with AsyncSessionLocal() as session:
        yield session


@strawberry.type
class Query:
    @strawberry.field
    async def client(self, info: Info, client_id: int) -> Optional[ClientType]:
        """Get client by ID"""
        async with AsyncSessionLocal() as db:
            result = await db.execute(
                select(Client).where(Client.id == client_id)
            )
            client = result.scalar_one_or_none()
            if client:
                return ClientType(
                    id=client.id,
                    company_name=client.company_name,
                    company_email=client.company_email,
                    company_phone=client.company_phone,
                    company_website=client.company_website,
                    industry=client.industry,
                    company_size=client.company_size,
                    contact_name=client.contact_name,
                    contact_email=client.contact_email,
                    status=client.status.value,
                    chatbot_name=client.chatbot_name,
                    chatbot_welcome_message=client.chatbot_welcome_message,
                    monthly_message_limit=client.monthly_message_limit,
                    daily_message_limit=client.daily_message_limit,
                    created_at=client.created_at
                )
            return None

    @strawberry.field
    async def plans(self, info: Info, active_only: bool = True) -> List[PlanType]:
        """Get all subscription plans"""
        async with AsyncSessionLocal() as db:
            query = select(Plan)
            if active_only:
                query = query.where(Plan.is_active == True)
            result = await db.execute(query)
            plans = result.scalars().all()
            return [
                PlanType(
                    id=plan.id,
                    name=plan.name,
                    description=plan.description,
                    price_monthly=float(plan.price_monthly),
                    price_yearly=float(plan.price_yearly),
                    monthly_messages=plan.monthly_messages,
                    max_chatbots=plan.max_chatbots,
                    features=plan.features or [],
                    is_active=plan.is_active
                )
                for plan in plans
            ]

    @strawberry.field
    async def conversations(
        self,
        info: Info,
        client_id: int,
        filters: Optional[ConversationFilterInput] = None,
        pagination: Optional[PaginationInput] = None
    ) -> ConversationConnection:
        """Get conversations with filtering and pagination"""
        async with AsyncSessionLocal() as db:
            page = pagination.page if pagination else 1
            per_page = pagination.per_page if pagination else 20

            query = select(Conversation).where(Conversation.client_id == client_id)

            if filters:
                if filters.status:
                    query = query.where(Conversation.status == filters.status)
                if filters.channel:
                    query = query.where(Conversation.channel == filters.channel)
                if filters.visitor_email:
                    query = query.where(Conversation.visitor_email == filters.visitor_email)
                if filters.start_date:
                    query = query.where(Conversation.started_at >= filters.start_date)
                if filters.end_date:
                    query = query.where(Conversation.started_at <= filters.end_date)

            # Get total count
            count_result = await db.execute(query)
            total = len(count_result.scalars().all())

            # Apply pagination
            query = query.offset((page - 1) * per_page).limit(per_page)
            result = await db.execute(query)
            conversations = result.scalars().all()

            total_pages = (total + per_page - 1) // per_page

            return ConversationConnection(
                items=[
                    ConversationType(
                        id=conv.id,
                        client_id=conv.client_id,
                        session_id=conv.session_id,
                        visitor_id=conv.visitor_id,
                        visitor_name=conv.visitor_name,
                        visitor_email=conv.visitor_email,
                        channel=conv.channel.value if hasattr(conv.channel, 'value') else conv.channel,
                        status=conv.status.value if hasattr(conv.status, 'value') else conv.status,
                        message_count=conv.message_count,
                        started_at=conv.started_at,
                        ended_at=conv.ended_at
                    )
                    for conv in conversations
                ],
                pagination=PaginationInfo(
                    total=total,
                    page=page,
                    per_page=per_page,
                    total_pages=total_pages,
                    has_next=page < total_pages,
                    has_prev=page > 1
                )
            )

    @strawberry.field
    async def messages(
        self,
        info: Info,
        conversation_id: int,
        pagination: Optional[PaginationInput] = None
    ) -> MessageConnection:
        """Get messages for a conversation"""
        async with AsyncSessionLocal() as db:
            page = pagination.page if pagination else 1
            per_page = pagination.per_page if pagination else 50

            query = select(Message).where(Message.conversation_id == conversation_id)

            count_result = await db.execute(query)
            total = len(count_result.scalars().all())

            query = query.order_by(Message.created_at).offset((page - 1) * per_page).limit(per_page)
            result = await db.execute(query)
            messages = result.scalars().all()

            total_pages = (total + per_page - 1) // per_page

            return MessageConnection(
                items=[
                    MessageType(
                        id=msg.id,
                        conversation_id=msg.conversation_id,
                        role=msg.role.value if hasattr(msg.role, 'value') else msg.role,
                        content=msg.content,
                        tokens_used=msg.tokens_used or 0,
                        created_at=msg.created_at
                    )
                    for msg in messages
                ],
                pagination=PaginationInfo(
                    total=total,
                    page=page,
                    per_page=per_page,
                    total_pages=total_pages,
                    has_next=page < total_pages,
                    has_prev=page > 1
                )
            )

    @strawberry.field
    async def invoices(
        self,
        info: Info,
        client_id: int,
        status: Optional[str] = None,
        pagination: Optional[PaginationInput] = None
    ) -> InvoiceConnection:
        """Get invoices for a client"""
        async with AsyncSessionLocal() as db:
            page = pagination.page if pagination else 1
            per_page = pagination.per_page if pagination else 20

            query = select(Invoice).where(Invoice.client_id == client_id)
            if status:
                query = query.where(Invoice.status == status)

            count_result = await db.execute(query)
            total = len(count_result.scalars().all())

            query = query.order_by(Invoice.created_at.desc()).offset((page - 1) * per_page).limit(per_page)
            result = await db.execute(query)
            invoices = result.scalars().all()

            total_pages = (total + per_page - 1) // per_page

            return InvoiceConnection(
                items=[
                    InvoiceType(
                        id=inv.id,
                        client_id=inv.client_id,
                        invoice_number=inv.invoice_number,
                        amount=float(inv.amount),
                        tax_amount=float(inv.tax_amount or 0),
                        total_amount=float(inv.total_amount),
                        currency=inv.currency,
                        status=inv.status.value if hasattr(inv.status, 'value') else inv.status,
                        due_date=inv.due_date,
                        paid_at=inv.paid_at,
                        created_at=inv.created_at
                    )
                    for inv in invoices
                ],
                pagination=PaginationInfo(
                    total=total,
                    page=page,
                    per_page=per_page,
                    total_pages=total_pages,
                    has_next=page < total_pages,
                    has_prev=page > 1
                )
            )

    @strawberry.field
    async def webhooks(self, info: Info, client_id: int) -> List[WebhookType]:
        """Get webhooks for a client"""
        async with AsyncSessionLocal() as db:
            query = select(ClientWebhook).where(
                ClientWebhook.client_id == client_id,
                ClientWebhook.deleted_at.is_(None)
            )
            result = await db.execute(query)
            webhooks = result.scalars().all()
            return [
                WebhookType(
                    id=wh.id,
                    name=wh.name,
                    url=wh.url,
                    events=wh.events,
                    is_active=wh.is_active,
                    total_deliveries=wh.total_deliveries,
                    successful_deliveries=wh.successful_deliveries,
                    failed_deliveries=wh.failed_deliveries,
                    created_at=wh.created_at
                )
                for wh in webhooks
            ]

    @strawberry.field
    async def usage_stats(
        self,
        info: Info,
        client_id: int,
        start_date: datetime,
        end_date: datetime
    ) -> UsageStatsType:
        """Get usage statistics for a client"""
        async with AsyncSessionLocal() as db:
            # Get conversation stats
            conv_query = select(Conversation).where(
                Conversation.client_id == client_id,
                Conversation.started_at >= start_date,
                Conversation.started_at <= end_date
            )
            conv_result = await db.execute(conv_query)
            conversations = conv_result.scalars().all()

            total_messages = sum(c.message_count for c in conversations)
            total_conversations = len(conversations)

            # Get token usage from messages
            msg_query = select(Message).join(Conversation).where(
                Conversation.client_id == client_id,
                Message.created_at >= start_date,
                Message.created_at <= end_date
            )
            msg_result = await db.execute(msg_query)
            messages = msg_result.scalars().all()
            total_tokens = sum(m.tokens_used or 0 for m in messages)

            return UsageStatsType(
                total_messages=total_messages,
                total_conversations=total_conversations,
                total_tokens=total_tokens,
                period_start=start_date,
                period_end=end_date
            )


# ============== Mutation Types ==============

@strawberry.type
class MutationResponse:
    success: bool
    message: str
    id: Optional[int] = None


@strawberry.input
class UpdateClientInput:
    company_name: Optional[str] = None
    company_phone: Optional[str] = None
    company_website: Optional[str] = None
    chatbot_name: Optional[str] = None
    chatbot_welcome_message: Optional[str] = None
    chatbot_system_prompt: Optional[str] = None


@strawberry.input
class CreateWebhookInput:
    name: str
    url: str
    events: List[str]


@strawberry.type
class Mutation:
    @strawberry.mutation
    async def update_client(
        self,
        info: Info,
        client_id: int,
        input: UpdateClientInput
    ) -> MutationResponse:
        """Update client information"""
        async with AsyncSessionLocal() as db:
            result = await db.execute(
                select(Client).where(Client.id == client_id)
            )
            client = result.scalar_one_or_none()

            if not client:
                return MutationResponse(success=False, message="Client not found")

            if input.company_name:
                client.company_name = input.company_name
            if input.company_phone:
                client.company_phone = input.company_phone
            if input.company_website:
                client.company_website = input.company_website
            if input.chatbot_name:
                client.chatbot_name = input.chatbot_name
            if input.chatbot_welcome_message:
                client.chatbot_welcome_message = input.chatbot_welcome_message
            if input.chatbot_system_prompt:
                client.chatbot_system_prompt = input.chatbot_system_prompt

            await db.commit()
            return MutationResponse(success=True, message="Client updated successfully", id=client.id)

    @strawberry.mutation
    async def end_conversation(
        self,
        info: Info,
        conversation_id: int
    ) -> MutationResponse:
        """End an active conversation"""
        async with AsyncSessionLocal() as db:
            result = await db.execute(
                select(Conversation).where(Conversation.id == conversation_id)
            )
            conversation = result.scalar_one_or_none()

            if not conversation:
                return MutationResponse(success=False, message="Conversation not found")

            conversation.status = "closed"
            conversation.ended_at = datetime.utcnow()
            await db.commit()

            return MutationResponse(success=True, message="Conversation ended", id=conversation.id)


# ============== Create Schema ==============

schema = strawberry.Schema(query=Query, mutation=Mutation)

# Create GraphQL router
graphql_router = GraphQLRouter(schema)
