import json
import logging
from contextlib import suppress

from django.conf import settings
from django.contrib.auth.decorators import login_not_required
from django.core.mail import send_mail
from django.db import transaction
from django.http import JsonResponse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt

from servala.api.permissions import OSBBasicAuthPermission
from servala.core.exoscale import get_exoscale_origin
from servala.core.models import BillingEntity, Organization, User
from servala.core.models.service import Service, ServiceOffering

logger = logging.getLogger(__name__)


@method_decorator(csrf_exempt, name="dispatch")
@method_decorator(login_not_required, name="dispatch")
class OSBServiceInstanceView(OSBBasicAuthPermission, View):
    """
    OSB API endpoint for service instance provisioning (onboarding).
    Implements the PUT /v2/service_instances/:instance_id endpoint.
    https://docs.servala.com/exoscale-osb.html#_onboarding
    """

    def _error(self, error):
        return JsonResponse({"error": error}, status=400)

    def _get_user(self, data):
        email = data.get("email", "").strip().lower()
        if not email:
            raise ValueError("Email address is required but missing or empty")

        full_name = data.get("full_name") or ""
        name_parts = full_name.split(" ", 1)
        first_name = name_parts[0] if name_parts else ""
        last_name = name_parts[1] if len(name_parts) > 1 else ""
        user, _ = User.objects.get_or_create(
            email=email,
            defaults={"first_name": first_name, "last_name": last_name},
        )
        return user

    def put(self, request, instance_id):
        """
        This implements the Exoscale onboarding flow.
        https://docs.servala.com/exoscale-osb.html#_onboarding
        https://community.exoscale.com/vendor/marketplace-managed-svc-prov/#provisioning
        """
        try:
            data = json.loads(request.body)
        except json.JSONDecodeError:
            return JsonResponse({"error": "Invalid JSON in request body"}, status=400)

        context = data.get("context", {})
        parameters = data.get("parameters", {})

        organization_guid = context.get("organization_guid") or data.get(
            "organization_guid"
        )
        organization_name = context.get("organization_name")
        organization_display_name = context.get(
            "organization_display_name", organization_name
        )
        users = parameters.get("users", [])
        service_id = data.get("service_id")
        plan_id = data.get("plan_id")

        if not organization_guid:
            return self._error("organization_guid is required but missing.")
        if not organization_name:
            return self._error("organization_name is required but missing.")
        if not users:
            return self._error("users array is required but missing.")
        if len(users) != 1:
            return self._error("users array is expected to contain a single user.")
        if not service_id:
            return self._error("service_id is required but missing.")
        if not plan_id:
            return self._error("plan_id is required but missing.")

        try:
            user = self._get_user(users[0])
        except Exception as e:
            return self._error(f"Unable to create user: {e}")

        try:
            service = Service.objects.get(osb_service_id=service_id)
            service_offering = ServiceOffering.objects.get(
                osb_plan_id=plan_id, service=service
            )
        except Service.DoesNotExist:
            return self._error(f"Unknown service_id: {service_id}")
        except ServiceOffering.DoesNotExist:
            return self._error(
                f"Unknown plan_id: {plan_id} for service_id: {service_id}"
            )

        exoscale_origin = get_exoscale_origin()
        with suppress(Organization.DoesNotExist):
            organization = Organization.objects.get(
                osb_guid=organization_guid, origin=exoscale_origin
            )
            self._send_service_welcome_email(
                request, organization, user, service, service_offering
            )
            return JsonResponse({"message": "Service already enabled"}, status=200)

        odoo_data = {
            "company_name": organization_display_name,
            "invoice_email": user.email,
        }
        try:
            with transaction.atomic():
                billing_entity = BillingEntity.create_from_data(
                    name=f"{organization_display_name} (Exoscale)", odoo_data=odoo_data
                )
                organization = Organization(
                    name=organization_display_name,
                    billing_entity=billing_entity,
                    origin=exoscale_origin,
                    osb_guid=organization_guid,
                )
                organization = Organization.create_organization(organization, user)

                self._send_invitation_email(request, organization, user)
                self._send_service_welcome_email(
                    request, organization, user, service, service_offering
                )

                return JsonResponse(
                    {"message": "Successfully enabled service"}, status=201
                )

        except Exception as e:
            logger.error(f"Error creating organization for Exoscale: {str(e)}")
            return JsonResponse({"error": "Internal server error"}, status=500)

    def _send_invitation_email(self, request, organization, user):
        subject = f"Welcome to Servala - {organization.name}"
        url = request.build_absolute_uri(organization.urls.base)
        message = f"""Hello {user.first_name or user.email},

You have been invited to join the organization "{organization.name}" on Servala Portal.

You can access your organization at: {url}

Please use this email address ({user.email}) when prompted to log in.

Best regards,
The Servala Team"""

        send_mail(
            subject=subject,
            message=message,
            from_email=settings.EMAIL_DEFAULT_FROM,
            recipient_list=[user.email],
            fail_silently=False,
        )

    def _send_service_welcome_email(
        self, request, organization, user, service, service_offering
    ):
        service_path = f"{organization.urls.services}{service.slug}/offering/{service_offering.id}/"
        service_url = request.build_absolute_uri(service_path)

        subject = f"Get started with {service.name} - {organization.name}"
        message = f"""Hello {user.first_name or user.email},

Your organization "{organization.name}" is now ready on Servala Portal!

You can create your {service.name} service directly here:
{service_url}

Or browse all available services at: {request.build_absolute_uri(organization.urls.services)}

Need help? Contact our support team through the portal.

Best regards,
The Servala Team"""

        send_mail(
            subject=subject,
            message=message,
            from_email=settings.EMAIL_DEFAULT_FROM,
            recipient_list=[user.email],
            fail_silently=False,
        )
