"""
POS Transaction Service - Business Logic Layer
"""

from typing import List, Optional, Dict, Any
from uuid import UUID
from datetime import datetime
from decimal import Decimal
import structlog

from app.repositories.pos_transaction_repository import POSTransactionRepository
from app.repositories.pos_transaction_item_repository import POSTransactionItemRepository
from app.schemas.pos_transaction import (
    POSTransactionResponse,
    POSTransactionDashboardSummary
)
from app.core.database import get_db_transaction

logger = structlog.get_logger()


class POSTransactionService:
    """Service layer for POS transaction operations"""

    def __init__(self):
        pass

    async def get_transactions_by_tenant(
        self,
        tenant_id: UUID,
        pos_system: Optional[str] = None,
        start_date: Optional[datetime] = None,
        end_date: Optional[datetime] = None,
        status: Optional[str] = None,
        is_synced: Optional[bool] = None,
        skip: int = 0,
        limit: int = 50
    ) -> List[POSTransactionResponse]:
        """Get POS transactions for a tenant with filtering"""
        try:
            async with get_db_transaction() as db:
                repository = POSTransactionRepository(db)

                transactions = await repository.get_transactions_by_tenant(
                    tenant_id=tenant_id,
                    pos_system=pos_system,
                    start_date=start_date,
                    end_date=end_date,
                    status=status,
                    is_synced=is_synced,
                    skip=skip,
                    limit=limit
                )

                # Convert to response schemas
                responses = []
                for transaction in transactions:
                    response = POSTransactionResponse.from_orm(transaction)
                    responses.append(response)

                return responses

        except Exception as e:
            logger.error("Failed to get transactions by tenant", error=str(e), tenant_id=tenant_id)
            raise

    async def count_transactions_by_tenant(
        self,
        tenant_id: UUID,
        pos_system: Optional[str] = None,
        start_date: Optional[datetime] = None,
        end_date: Optional[datetime] = None,
        status: Optional[str] = None,
        is_synced: Optional[bool] = None
    ) -> int:
        """Count POS transactions for a tenant with filtering"""
        try:
            async with get_db_transaction() as db:
                repository = POSTransactionRepository(db)

                count = await repository.count_transactions_by_tenant(
                    tenant_id=tenant_id,
                    pos_system=pos_system,
                    start_date=start_date,
                    end_date=end_date,
                    status=status,
                    is_synced=is_synced
                )

                return count

        except Exception as e:
            logger.error("Failed to count transactions by tenant", error=str(e), tenant_id=tenant_id)
            raise

    async def get_transaction_with_items(
        self,
        transaction_id: UUID,
        tenant_id: UUID
    ) -> Optional[POSTransactionResponse]:
        """Get transaction with all its items"""
        try:
            async with get_db_transaction() as db:
                repository = POSTransactionRepository(db)

                transaction = await repository.get_transaction_with_items(
                    transaction_id=transaction_id,
                    tenant_id=tenant_id
                )

                if not transaction:
                    return None

                return POSTransactionResponse.from_orm(transaction)

        except Exception as e:
            logger.error("Failed to get transaction with items",
                        transaction_id=str(transaction_id),
                        error=str(e))
            raise

    async def get_dashboard_summary(
        self,
        tenant_id: UUID
    ) -> POSTransactionDashboardSummary:
        """Get dashboard summary for POS transactions"""
        try:
            async with get_db_transaction() as db:
                repository = POSTransactionRepository(db)

                # Get metrics from repository
                metrics = await repository.get_dashboard_metrics(tenant_id)

                # Get sync status
                sync_status = await repository.get_sync_status_summary(tenant_id)

                # Construct dashboard summary
                return POSTransactionDashboardSummary(
                    total_transactions_today=metrics["total_transactions_today"],
                    total_transactions_this_week=metrics["total_transactions_this_week"],
                    total_transactions_this_month=metrics["total_transactions_this_month"],
                    revenue_today=Decimal(str(metrics["revenue_today"])),
                    revenue_this_week=Decimal(str(metrics["revenue_this_week"])),
                    revenue_this_month=Decimal(str(metrics["revenue_this_month"])),
                    average_transaction_value=Decimal(str(metrics["average_transaction_value"])),
                    status_breakdown=metrics["status_breakdown"],
                    payment_method_breakdown=metrics["payment_method_breakdown"],
                    sync_status=sync_status
                )

        except Exception as e:
            logger.error("Failed to get dashboard summary", error=str(e), tenant_id=tenant_id)
            raise

    async def get_sync_metrics(
        self,
        tenant_id: UUID
    ) -> Dict[str, Any]:
        """Get sync metrics for transactions"""
        try:
            async with get_db_transaction() as db:
                repository = POSTransactionRepository(db)

                sync_status = await repository.get_sync_status_summary(tenant_id)

                # Calculate sync rate
                total = sync_status["synced"] + sync_status["pending"] + sync_status["failed"]
                sync_rate = (sync_status["synced"] / total * 100) if total > 0 else 0

                return {
                    "sync_status": sync_status,
                    "sync_rate_percentage": round(sync_rate, 2),
                    "total_transactions": total
                }

        except Exception as e:
            logger.error("Failed to get sync metrics", error=str(e), tenant_id=tenant_id)
            raise

    async def calculate_transaction_analytics(
        self,
        tenant_id: UUID,
        start_date: datetime,
        end_date: datetime
    ) -> Dict[str, Any]:
        """Calculate analytics for transactions within a date range"""
        try:
            async with get_db_transaction() as db:
                repository = POSTransactionRepository(db)

                transactions = await repository.get_transactions_by_date_range(
                    tenant_id=tenant_id,
                    start_date=start_date.date(),
                    end_date=end_date.date(),
                    skip=0,
                    limit=10000  # Large limit for analytics
                )

                # Calculate analytics
                total_revenue = Decimal("0")
                total_transactions = len(transactions)
                payment_methods = {}
                order_types = {}
                hourly_distribution = {}

                for transaction in transactions:
                    if transaction.status == "completed":
                        total_revenue += transaction.total_amount

                        # Payment method breakdown
                        pm = transaction.payment_method or "unknown"
                        payment_methods[pm] = payment_methods.get(pm, 0) + 1

                        # Order type breakdown
                        ot = transaction.order_type or "unknown"
                        order_types[ot] = order_types.get(ot, 0) + 1

                        # Hourly distribution
                        hour = transaction.transaction_date.hour
                        hourly_distribution[hour] = hourly_distribution.get(hour, 0) + 1

                avg_transaction_value = (total_revenue / total_transactions) if total_transactions > 0 else Decimal("0")

                return {
                    "period": {
                        "start_date": start_date.isoformat(),
                        "end_date": end_date.isoformat()
                    },
                    "total_revenue": float(total_revenue),
                    "total_transactions": total_transactions,
                    "average_transaction_value": float(avg_transaction_value),
                    "payment_methods": payment_methods,
                    "order_types": order_types,
                    "hourly_distribution": hourly_distribution
                }

        except Exception as e:
            logger.error("Failed to calculate transaction analytics", error=str(e), tenant_id=tenant_id)
            raise

    async def sync_transaction_to_sales(
        self,
        transaction_id: UUID,
        tenant_id: UUID
    ) -> Dict[str, Any]:
        """
        Sync a single POS transaction to the sales service

        Args:
            transaction_id: Transaction UUID
            tenant_id: Tenant UUID

        Returns:
            Dict with sync status and details
        """
        try:
            from shared.clients.sales_client import SalesServiceClient
            from app.core.config import settings

            async with get_db_transaction() as db:
                transaction_repo = POSTransactionRepository(db)
                items_repo = POSTransactionItemRepository(db)

                # Get transaction
                transaction = await transaction_repo.get_by_id(transaction_id)
                if not transaction or transaction.tenant_id != tenant_id:
                    return {
                        "success": False,
                        "error": "Transaction not found or unauthorized"
                    }

                # Check if already synced
                if transaction.is_synced_to_sales:
                    logger.info("Transaction already synced to sales",
                              transaction_id=transaction_id,
                              sales_record_id=transaction.sales_record_id)
                    return {
                        "success": True,
                        "already_synced": True,
                        "sales_record_id": str(transaction.sales_record_id)
                    }

                # Get transaction items
                items = await items_repo.get_by_transaction_id(transaction_id)

                # Initialize sales client
                sales_client = SalesServiceClient(settings, calling_service_name="pos")

                # Create sales records for each item
                sales_record_ids = []
                failed_items = []

                for item in items:
                    try:
                        sales_data = {
                            "inventory_product_id": str(item.product_id) if item.product_id else None,
                            "product_name": item.product_name,
                            "product_category": "finished_product",
                            "quantity_sold": float(item.quantity),
                            "unit_price": float(item.unit_price),
                            "total_amount": float(item.subtotal),
                            "sale_date": transaction.transaction_date.strftime("%Y-%m-%d"),
                            "sales_channel": "pos",
                            "source": f"pos_sync_{transaction.pos_system}",
                            "payment_method": transaction.payment_method or "unknown",
                            "notes": f"POS Transaction: {transaction.external_transaction_id or transaction_id}"
                        }

                        result = await sales_client.create_sales_record(
                            tenant_id=str(tenant_id),
                            sales_data=sales_data
                        )

                        if result and result.get("id"):
                            sales_record_ids.append(result["id"])
                            logger.info("Synced item to sales",
                                      transaction_id=transaction_id,
                                      item_id=item.id,
                                      sales_record_id=result["id"])
                        else:
                            failed_items.append({
                                "item_id": str(item.id),
                                "product_name": item.product_name,
                                "error": "No sales record ID returned"
                            })

                    except Exception as item_error:
                        logger.error("Failed to sync item to sales",
                                   error=str(item_error),
                                   transaction_id=transaction_id,
                                   item_id=item.id)
                        failed_items.append({
                            "item_id": str(item.id),
                            "product_name": item.product_name,
                            "error": str(item_error)
                        })

                # Update transaction sync status
                if sales_record_ids and len(failed_items) == 0:
                    # Full success
                    transaction.is_synced_to_sales = True
                    transaction.sales_record_id = UUID(sales_record_ids[0])  # Store first record ID
                    transaction.sync_completed_at = datetime.utcnow()
                    await db.commit()

                    logger.info("Transaction fully synced to sales",
                              transaction_id=transaction_id,
                              items_synced=len(sales_record_ids))

                    return {
                        "success": True,
                        "items_synced": len(sales_record_ids),
                        "sales_record_ids": sales_record_ids,
                        "failed_items": []
                    }

                elif sales_record_ids and len(failed_items) > 0:
                    # Partial success
                    transaction.sync_attempted_at = datetime.utcnow()
                    transaction.sync_error = f"Partial sync: {len(failed_items)} items failed"
                    transaction.sync_retry_count = (transaction.sync_retry_count or 0) + 1
                    await db.commit()

                    logger.warning("Transaction partially synced to sales",
                                 transaction_id=transaction_id,
                                 items_synced=len(sales_record_ids),
                                 items_failed=len(failed_items))

                    return {
                        "success": False,
                        "partial_success": True,
                        "items_synced": len(sales_record_ids),
                        "sales_record_ids": sales_record_ids,
                        "failed_items": failed_items
                    }

                else:
                    # Complete failure
                    transaction.sync_attempted_at = datetime.utcnow()
                    transaction.sync_error = "All items failed to sync"
                    transaction.sync_retry_count = (transaction.sync_retry_count or 0) + 1
                    await db.commit()

                    logger.error("Transaction sync failed completely",
                               transaction_id=transaction_id,
                               items_failed=len(failed_items))

                    return {
                        "success": False,
                        "items_synced": 0,
                        "failed_items": failed_items
                    }

        except Exception as e:
            logger.error("Failed to sync transaction to sales",
                       error=str(e),
                       transaction_id=transaction_id,
                       tenant_id=tenant_id)
            return {
                "success": False,
                "error": str(e)
            }

    async def sync_unsynced_transactions(
        self,
        tenant_id: UUID,
        limit: int = 50
    ) -> Dict[str, Any]:
        """
        Sync all unsynced transactions to the sales service

        Args:
            tenant_id: Tenant UUID
            limit: Maximum number of transactions to sync in one batch

        Returns:
            Dict with sync summary
        """
        try:
            async with get_db_transaction() as db:
                repository = POSTransactionRepository(db)

                # Get unsynced transactions
                unsynced_transactions = await repository.get_transactions_by_tenant(
                    tenant_id=tenant_id,
                    is_synced=False,
                    status="completed",  # Only sync completed transactions
                    limit=limit
                )

                if not unsynced_transactions:
                    logger.info("No unsynced transactions found", tenant_id=tenant_id)
                    return {
                        "success": True,
                        "total_transactions": 0,
                        "synced": 0,
                        "failed": 0
                    }

                synced_count = 0
                failed_count = 0
                results = []

                for transaction in unsynced_transactions:
                    result = await self.sync_transaction_to_sales(
                        transaction.id,
                        tenant_id
                    )

                    if result.get("success"):
                        synced_count += 1
                    else:
                        failed_count += 1

                    results.append({
                        "transaction_id": str(transaction.id),
                        "external_id": transaction.external_transaction_id,
                        "result": result
                    })

                logger.info("Batch sync completed",
                          tenant_id=tenant_id,
                          total=len(unsynced_transactions),
                          synced=synced_count,
                          failed=failed_count)

                return {
                    "success": True,
                    "total_transactions": len(unsynced_transactions),
                    "synced": synced_count,
                    "failed": failed_count,
                    "results": results
                }

        except Exception as e:
            logger.error("Failed to batch sync transactions",
                       error=str(e),
                       tenant_id=tenant_id)
            return {
                "success": False,
                "error": str(e)
            }
