"""
M2M (Machine-to-Machine) GraphQL schema for Billing service.
Used by internal services (Temporal workflows, etc.) without user authentication.
"""
import graphene
import uuid
import logging
from graphene_django import DjangoObjectType

import tigerbeetle as tb
from ..tigerbeetle_client import tigerbeetle_client
from ..models import Account as AccountModel, TransactionReason as TransactionReasonModel, Operation as OperationModel

logger = logging.getLogger(__name__)


def get_or_create_local_accounts(account_ids_str: list[str]) -> tuple[list[AccountModel], list[str]]:
    """
    Checks for local account existence, returns a list of existing accounts
    and a list of IDs for accounts that need to be created.
    """
    existing_accounts = list(AccountModel.objects.filter(uuid__in=[uuid.UUID(id_str) for id_str in account_ids_str]))
    existing_ids = {str(acc.uuid) for acc in existing_accounts}
    new_ids = [id_str for id_str in account_ids_str if id_str not in existing_ids]
    return existing_accounts, new_ids


class TransactionReasonType(DjangoObjectType):
    class Meta:
        model = TransactionReasonModel
        fields = "__all__"


class OperationType(DjangoObjectType):
    class Meta:
        model = OperationModel
        fields = "__all__"


class CreateTransferInput(graphene.InputObjectType):
    fromAccountId = graphene.String(required=True, description="Source account UUID")
    toAccountId = graphene.String(required=True, description="Destination account UUID")
    amount = graphene.Int(required=True, description="Amount in kopecks")
    code = graphene.Int(required=True, description="Operation code for inter-service coordination")


class CreateTransfer(graphene.Mutation):
    """
    Creates a financial transfer between two accounts.
    If the accounts do not exist in TigerBeetle, they will be created automatically.
    """
    class Arguments:
        input = CreateTransferInput(required=True)

    success = graphene.Boolean()
    message = graphene.String()
    transferId = graphene.String()

    def mutate(self, info, input):
        # Hardcoded ledger for RUB
        LEDGER = 1

        # 1. Validate UUIDs
        try:
            from_uuid = uuid.UUID(input.fromAccountId)
            to_uuid = uuid.UUID(input.toAccountId)
        except ValueError:
            return CreateTransfer(success=False, message="Invalid account ID format. Must be a valid UUID.")

        # 2. Check for local account existence and identify new accounts
        all_ids_str = [input.fromAccountId, input.toAccountId]
        _, new_account_ids_str = get_or_create_local_accounts(all_ids_str)

        accounts_to_create = []
        if new_account_ids_str:
            logger.info(f"Accounts to create in TigerBeetle: {new_account_ids_str}")
            for id_str in new_account_ids_str:
                account_uuid = uuid.UUID(id_str)
                # Create in local DB first
                AccountModel.objects.create(
                    uuid=account_uuid,
                    name=f"Team {id_str[:8]}"
                )
                # Prepare for TigerBeetle
                accounts_to_create.append(
                    tb.Account(
                        id=(uuid.UUID(id_str)).int,
                        ledger=LEDGER,
                        code=100,  # Default code for a team/user account
                    )
                )

        # 3. Create accounts in TigerBeetle if any are new
        if accounts_to_create:
            account_errors = tigerbeetle_client.create_accounts(accounts_to_create)
            if account_errors:
                error_details = [e.code.name if hasattr(e, 'code') else str(e) for e in account_errors]
                error_msg = f"Failed to create new accounts in TigerBeetle: {error_details}"
                logger.error(error_msg)
                return CreateTransfer(success=False, message=error_msg)

        # 4. Prepare and execute the transfer
        transfer_id_obj = uuid.uuid4()
        transfer = tb.Transfer(
            id=transfer_id_obj.int,
            debit_account_id=from_uuid.int,
            credit_account_id=to_uuid.int,
            amount=input.amount,
            ledger=LEDGER,
            code=input.code,
        )

        transfer_errors = tigerbeetle_client.create_transfers([transfer])
        if transfer_errors:
            error_details = [e.code.name if hasattr(e, 'code') else str(e) for e in transfer_errors]
            error_msg = f"Transfer failed: {error_details}"
            logger.error(error_msg)
            return CreateTransfer(success=False, message=error_msg)

        logger.info(f"Transfer {transfer_id_obj} completed successfully.")
        return CreateTransfer(
            success=True,
            message="Transfer completed successfully.",
            transferId=str(transfer_id_obj)
        )


class M2MQuery(graphene.ObjectType):
    transactionReasons = graphene.List(TransactionReasonType)
    transactionReason = graphene.Field(TransactionReasonType, uuid=graphene.UUID(required=True))
    operationTypes = graphene.List(OperationType)

    def resolve_transactionReasons(self, info, **kwargs):
        return TransactionReasonModel.objects.all()

    def resolve_transactionReason(self, info, uuid):
        try:
            return TransactionReasonModel.objects.get(pk=uuid)
        except TransactionReasonModel.DoesNotExist:
            return None

    def resolve_operationTypes(self, info, **kwargs):
        return OperationModel.objects.all()


class M2MMutation(graphene.ObjectType):
    createTransfer = CreateTransfer.Field()


m2m_schema = graphene.Schema(query=M2MQuery, mutation=M2MMutation)
