package accountapiimp

import (
	"context"
	"errors"
	"net/http"

	"github.com/tech/sendico/pkg/api/http/response"
	"github.com/tech/sendico/pkg/merrors"
	"github.com/tech/sendico/pkg/model"
	"github.com/tech/sendico/pkg/mservice"
	"github.com/tech/sendico/pkg/mutil/mzap"
	"github.com/tech/sendico/server/interface/api/sresponse"
	"go.mongodb.org/mongo-driver/v2/bson"
	"go.uber.org/zap"
)

func (a *AccountAPI) deleteProfile(r *http.Request, account *model.Account, token *sresponse.TokenData) http.HandlerFunc {
	ctx := r.Context()

	// Get the current organization from the request context
	orgRef, err := a.getCurrentOrganizationRef(r)
	if err != nil {
		a.logger.Warn("Failed to get current organization", zap.Error(err), mzap.StorableRef(account))
		return response.BadRequest(a.logger, a.Name(), "invalid_organization_context", "Invalid organization context")
	}

	// Get the organization
	var org model.Organization
	if err := a.odb.Get(ctx, *account.GetID(), orgRef, &org); err != nil {
		a.logger.Error("Failed to fetch organization", zap.Error(err), mzap.StorableRef(account))
		return response.Auto(a.logger, a.Name(), err)
	}

	// Delete the account (this will check if it's the only member)
	if err := a.accService.DeleteAccount(ctx, &org, account.ID); err != nil {
		if errors.Is(err, merrors.ErrInvalidArg) {
			a.logger.Warn("Cannot delete account - validation failed", zap.Error(err), mzap.StorableRef(account))
			return response.BadRequest(a.logger, a.Name(), "validation_failed", err.Error())
		}
		a.logger.Error("Failed to delete account", zap.Error(err), mzap.StorableRef(account))
		return response.Auto(a.logger, a.Name(), err)
	}

	a.logger.Info("Account deleted successfully", mzap.StorableRef(account))
	return response.Success(a.logger)
}

func (a *AccountAPI) deleteOrganization(r *http.Request, account *model.Account, token *sresponse.TokenData) http.HandlerFunc {
	ctx := r.Context()

	// Get the current organization from the request context
	orgRef, err := a.getCurrentOrganizationRef(r)
	if err != nil {
		a.logger.Warn("Failed to get current organization", zap.Error(err), mzap.StorableRef(account))
		return response.BadRequest(a.logger, a.Name(), "invalid_organization_context", "Invalid organization context")
	}

	// Get the organization
	var org model.Organization
	if err := a.odb.Get(ctx, *account.GetID(), orgRef, &org); err != nil {
		a.logger.Error("Failed to fetch organization", zap.Error(err), mzap.StorableRef(account))
		return response.Auto(a.logger, a.Name(), err)
	}

	// Delete the organization and all its data
	if err := a.accService.DeleteOrganization(ctx, &org); err != nil {
		a.logger.Error("Failed to delete organization", zap.Error(err), mzap.StorableRef(&org))
		return response.Auto(a.logger, a.Name(), err)
	}

	a.logger.Info("Organization deleted successfully", mzap.StorableRef(&org))
	return response.Success(a.logger)
}

func (a *AccountAPI) deleteAll(r *http.Request, account *model.Account, token *sresponse.TokenData) http.HandlerFunc {
	ctx := r.Context()

	// Get the current organization from the request context
	orgRef, err := a.getCurrentOrganizationRef(r)
	if err != nil {
		a.logger.Warn("Failed to get current organization", zap.Error(err), mzap.StorableRef(account))
		return response.BadRequest(a.logger, a.Name(), "invalid_organization_context", "Invalid organization context")
	}

	// Get the organization
	var org model.Organization
	if err := a.odb.Get(ctx, *account.GetID(), orgRef, &org); err != nil {
		a.logger.Error("Failed to fetch organization", zap.Error(err), mzap.StorableRef(account))
		return response.Auto(a.logger, a.Name(), err)
	}

	// Get organization permission reference
	var orgPolicy model.PolicyDescription
	if err := a.plcdb.GetBuiltInPolicy(ctx, mservice.Organizations, &orgPolicy); err != nil {
		a.logger.Error("Failed to fetch organization policy", zap.Error(err), mzap.StorableRef(account))
		return response.Auto(a.logger, a.Name(), err)
	}

	// Check if user has permission to delete the organization
	canDelete, err := a.enf.Enforce(ctx, orgPolicy.ID, account.ID, orgRef, bson.NilObjectID, model.ActionDelete)
	if err != nil {
		a.logger.Error("Failed to check delete permission", zap.Error(err), mzap.StorableRef(account))
		return response.Auto(a.logger, a.Name(), err)
	}
	if !canDelete {
		a.logger.Warn("User does not have permission to delete organization", mzap.StorableRef(account), mzap.StorableRef(&org))
		return response.AccessDenied(a.logger, a.Name(), "Insufficient permissions to delete organization")
	}

	// Delete everything (organization + account)
	if _, err := a.tf.CreateTransaction().Execute(ctx, func(c context.Context) (any, error) {
		if err := a.accService.DeleteAll(c, &org, account.ID); err != nil {
			a.logger.Warn("Failed to delete all data", zap.Error(err), mzap.StorableRef(&org), mzap.StorableRef(account))
			return nil, err
		}
		return struct{}{}, nil
	}); err != nil {
		a.logger.Warn("Failed to execute delete transaction", zap.Error(err), mzap.StorableRef(&org), mzap.StorableRef(account))
		return response.Auto(a.logger, a.Name(), err)
	}

	a.logger.Info("All data deleted successfully", mzap.StorableRef(&org), mzap.StorableRef(account))
	return response.Success(a.logger)
}

// Helper method to get current organization reference from request context
func (a *AccountAPI) getCurrentOrganizationRef(r *http.Request) (bson.ObjectID, error) {
	return a.oph.GetRef(r)
}
