"""
M2M (Machine-to-Machine) GraphQL schema.
Used by internal services (Temporal workflows, etc.) without user authentication.
"""
import graphene
import logging
from django.utils import timezone
from graphene_django import DjangoObjectType
from ..models import Team as TeamModel, TeamMember as TeamMemberModel, TeamAddress as TeamAddressModel, TeamInvitation as TeamInvitationModel, TeamInvitationToken as TeamInvitationTokenModel
from .user_schema import _get_or_create_user_with_profile

logger = logging.getLogger(__name__)


class Team(DjangoObjectType):
    id = graphene.String()

    class Meta:
        model = TeamModel
        fields = ('uuid', 'name', 'logto_org_id', 'created_at', 'updated_at')

    def resolve_id(self, info):
        return self.uuid


class M2MQuery(graphene.ObjectType):
    team = graphene.Field(Team, teamId=graphene.String(required=True))
    invitation = graphene.Field(lambda: TeamInvitation, invitationUuid=graphene.String(required=True))

    def resolve_team(self, info, teamId):
        try:
            return TeamModel.objects.get(uuid=teamId)
        except TeamModel.DoesNotExist:
            return None

    def resolve_invitation(self, info, invitationUuid):
        try:
            return TeamInvitationModel.objects.get(uuid=invitationUuid)
        except TeamInvitationModel.DoesNotExist:
            return None


class TeamInvitation(DjangoObjectType):
    class Meta:
        model = TeamInvitationModel
        fields = ('uuid', 'team', 'email', 'role', 'status', 'invited_by', 'expires_at', 'created_at')


class TeamInvitationToken(DjangoObjectType):
    class Meta:
        model = TeamInvitationTokenModel
        fields = ('uuid', 'invitation', 'workflow_status', 'expires_at', 'created_at')


class CreateInvitationFromWorkflowInput(graphene.InputObjectType):
    teamUuid = graphene.String(required=True)
    email = graphene.String(required=True)
    role = graphene.String()
    invitedBy = graphene.String(required=True)
    expiresAt = graphene.DateTime(required=True)


class CreateInvitationFromWorkflow(graphene.Mutation):
    class Arguments:
        input = CreateInvitationFromWorkflowInput(required=True)

    success = graphene.Boolean()
    message = graphene.String()
    invitationUuid = graphene.String()
    invitation = graphene.Field(TeamInvitation)

    def mutate(self, info, input):
        try:
            team = TeamModel.objects.get(uuid=input.teamUuid)
            invitation = TeamInvitationModel.objects.create(
                team=team,
                email=input.email,
                role=input.role or 'MEMBER',
                status='PENDING',
                invited_by=input.invitedBy,
                expires_at=input.expiresAt,
            )
            return CreateInvitationFromWorkflow(
                success=True,
                message="Invitation created",
                invitationUuid=invitation.uuid,
                invitation=invitation,
            )
        except Exception as exc:
            logger.exception("Failed to create invitation")
            return CreateInvitationFromWorkflow(success=False, message=str(exc))


class CreateInvitationTokenInput(graphene.InputObjectType):
    invitationUuid = graphene.String(required=True)
    tokenHash = graphene.String(required=True)
    expiresAt = graphene.DateTime(required=True)


class CreateInvitationToken(graphene.Mutation):
    class Arguments:
        input = CreateInvitationTokenInput(required=True)

    success = graphene.Boolean()
    message = graphene.String()
    token = graphene.Field(TeamInvitationToken)

    def mutate(self, info, input):
        try:
            invitation = TeamInvitationModel.objects.get(uuid=input.invitationUuid)
            token = TeamInvitationTokenModel.objects.create(
                invitation=invitation,
                token_hash=input.tokenHash,
                expires_at=input.expiresAt,
                workflow_status='pending',
            )
            return CreateInvitationToken(success=True, message="Token created", token=token)
        except Exception as exc:
            logger.exception("Failed to create invitation token")
            return CreateInvitationToken(success=False, message=str(exc))


class UpdateInvitationTokenStatusInput(graphene.InputObjectType):
    tokenUuid = graphene.String(required=True)
    status = graphene.String(required=True)  # pending | active | error


class UpdateInvitationTokenStatus(graphene.Mutation):
    class Arguments:
        input = UpdateInvitationTokenStatusInput(required=True)

    success = graphene.Boolean()
    message = graphene.String()
    token = graphene.Field(TeamInvitationToken)

    def mutate(self, info, input):
        try:
            token = TeamInvitationTokenModel.objects.get(uuid=input.tokenUuid)
            token.workflow_status = input.status
            token.save(update_fields=['workflow_status'])
            return UpdateInvitationTokenStatus(success=True, message="Status updated", token=token)
        except TeamInvitationTokenModel.DoesNotExist:
            return UpdateInvitationTokenStatus(success=False, message="Token not found")


class SetLogtoOrgIdMutation(graphene.Mutation):
    """Set Logto organization ID (used by Temporal workflows)"""
    class Arguments:
        teamId = graphene.String(required=True)
        logtoOrgId = graphene.String(required=True)

    team = graphene.Field(Team)
    success = graphene.Boolean()

    def mutate(self, info, teamId, logtoOrgId):
        try:
            team = TeamModel.objects.get(uuid=teamId)
            team.logto_org_id = logtoOrgId
            team.save(update_fields=['logto_org_id'])
            logger.info("Team %s: logto_org_id set to %s", teamId, logtoOrgId)
            return SetLogtoOrgIdMutation(team=team, success=True)
        except TeamModel.DoesNotExist:
            raise Exception(f"Team {teamId} not found")


class CreateAddressFromWorkflowMutation(graphene.Mutation):
    """Create TeamAddress from Temporal workflow (workflow-first pattern)"""
    class Arguments:
        workflowId = graphene.String(required=True)
        teamUuid = graphene.String(required=True)
        name = graphene.String(required=True)
        address = graphene.String(required=True)
        latitude = graphene.Float()
        longitude = graphene.Float()
        countryCode = graphene.String()
        isDefault = graphene.Boolean()

    success = graphene.Boolean()
    addressUuid = graphene.String()
    teamType = graphene.String()  # "buyer" or "seller"
    message = graphene.String()

    def mutate(self, info, workflowId, teamUuid, name, address,
               latitude=None, longitude=None, countryCode=None, isDefault=False):
        try:
            team = TeamModel.objects.get(uuid=teamUuid)

            # Если новый адрес default - сбрасываем старые
            if isDefault:
                team.addresses.update(is_default=False)

            address_obj = TeamAddressModel.objects.create(
                team=team,
                name=name,
                address=address,
                latitude=latitude,
                longitude=longitude,
                country_code=countryCode,
                is_default=isDefault,
                status='pending',
            )

            logger.info(
                "Created address %s for team %s (workflow=%s, team_type=%s)",
                address_obj.uuid, teamUuid, workflowId, team.team_type
            )

            return CreateAddressFromWorkflowMutation(
                success=True,
                addressUuid=str(address_obj.uuid),
                teamType=team.team_type.lower() if team.team_type else "buyer",
                message="Address created"
            )
        except TeamModel.DoesNotExist:
            return CreateAddressFromWorkflowMutation(
                success=False,
                message=f"Team {teamUuid} not found"
            )
        except Exception as e:
            logger.error("Failed to create address: %s", e)
            return CreateAddressFromWorkflowMutation(
                success=False,
                message=str(e)
            )


class CreateTeamFromWorkflowMutation(graphene.Mutation):
    """Create Team from Temporal workflow (KYC approval flow)"""
    class Arguments:
        teamName = graphene.String(required=True)
        ownerId = graphene.String(required=True)  # Logto user ID
        teamType = graphene.String()  # BUYER | SELLER, default BUYER
        countryCode = graphene.String()

    success = graphene.Boolean()
    teamId = graphene.String()
    teamUuid = graphene.String()
    message = graphene.String()

    def mutate(self, info, teamName, ownerId, teamType=None, countryCode=None):
        try:
            # Получаем или создаём пользователя
            owner = _get_or_create_user_with_profile(ownerId)

            # Создаём команду
            team = TeamModel.objects.create(
                name=teamName,
                owner=owner,
                team_type=teamType or 'BUYER'
            )

            # Добавляем owner как участника команды с ролью OWNER
            TeamMemberModel.objects.create(
                team=team,
                user=owner,
                role='OWNER'
            )

            # Устанавливаем как активную команду
            if hasattr(owner, 'profile') and not owner.profile.active_team:
                owner.profile.active_team = team
                owner.profile.save(update_fields=['active_team'])

            logger.info(
                "Created team %s (%s) for owner %s from workflow",
                team.uuid, teamName, ownerId
            )

            return CreateTeamFromWorkflowMutation(
                success=True,
                teamId=str(team.id),
                teamUuid=str(team.uuid),
                message="Team created"
            )
        except Exception as e:
            logger.error("Failed to create team from workflow: %s", e)
            return CreateTeamFromWorkflowMutation(
                success=False,
                message=str(e)
            )


class UpdateAddressStatusMutation(graphene.Mutation):
    """Update address processing status (used by Temporal workflows)"""
    class Arguments:
        addressUuid = graphene.String(required=True)
        status = graphene.String(required=True)  # pending, active, error
        errorMessage = graphene.String()

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

    def mutate(self, info, addressUuid, status, errorMessage=None):
        try:
            address = TeamAddressModel.objects.get(uuid=addressUuid)
            address.status = status
            update_fields = ['status', 'updated_at']

            if errorMessage:
                address.error_message = errorMessage
                update_fields.append('error_message')

            if status == 'active':
                address.processed_at = timezone.now()
                update_fields.append('processed_at')

            address.save(update_fields=update_fields)

            logger.info("Address %s status updated to %s", addressUuid, status)

            return UpdateAddressStatusMutation(success=True, message="Status updated")
        except TeamAddressModel.DoesNotExist:
            return UpdateAddressStatusMutation(
                success=False,
                message=f"Address {addressUuid} not found"
            )


class M2MMutation(graphene.ObjectType):
    setLogtoOrgId = SetLogtoOrgIdMutation.Field()
    createTeamFromWorkflow = CreateTeamFromWorkflowMutation.Field()
    createAddressFromWorkflow = CreateAddressFromWorkflowMutation.Field()
    updateAddressStatus = UpdateAddressStatusMutation.Field()
    createInvitationFromWorkflow = CreateInvitationFromWorkflow.Field()
    createInvitationToken = CreateInvitationToken.Field()
    updateInvitationTokenStatus = UpdateInvitationTokenStatus.Field()


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