package persistence

import (
	"context"
	"database/sql"

	apperrors "github.com/nexuscrm/kernel/pkg/errors"
	"github.com/nexuscrm/kernel/pkg/query"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

// ListCustomPermissions returns all custom permissions
func (r *PermissionRepository) ListCustomPermissions(ctx context.Context) ([]models.SystemCustomPermission, error) {
	qr := query.From(constants.TableCustomPermission).
		Select([]string{
			constants.FieldID,
			constants.FieldSysCustomPermission_APIName,
			constants.FieldSysCustomPermission_Label,
			constants.FieldSysCustomPermission_Description,
			constants.FieldSysCustomPermission_IsActive,
			constants.FieldSysCustomPermission_IsDeleted,
			constants.FieldCreatedDate,
		}).
		Build()

	rows, err := r.getExecutor(ctx).QueryContext(ctx, qr.SQL, qr.Params...)
	if err != nil {
		return nil, apperrors.NewInternalError("failed to query custom permissions", err)
	}
	defer rows.Close()

	var perms []models.SystemCustomPermission
	for rows.Next() {
		var p models.SystemCustomPermission
		if err := rows.Scan(&p.ID, &p.APIName, &p.Label, &p.Description, &p.IsActive, &p.IsDeleted, &p.CreatedDate); err != nil {
			return nil, apperrors.NewInternalError("failed to scan custom permission", err)
		}
		perms = append(perms, p)
	}
	if err := rows.Err(); err != nil {
		return nil, apperrors.NewInternalError("error iterating custom permissions", err)
	}

	return perms, nil
}

// GetCustomPermission returns a single custom permission by ID
func (r *PermissionRepository) GetCustomPermission(ctx context.Context, id string) (*models.SystemCustomPermission, error) {
	qr := query.From(constants.TableCustomPermission).
		Select([]string{
			constants.FieldID,
			constants.FieldSysCustomPermission_APIName,
			constants.FieldSysCustomPermission_Label,
			constants.FieldSysCustomPermission_Description,
			constants.FieldSysCustomPermission_IsActive,
			constants.FieldSysCustomPermission_IsDeleted,
			constants.FieldCreatedDate,
		}).
		Where(constants.FieldID, id).
		Build()

	var p models.SystemCustomPermission
	err := r.getExecutor(ctx).QueryRowContext(ctx, qr.SQL, qr.Params...).Scan(
		&p.ID, &p.APIName, &p.Label, &p.Description, &p.IsActive, &p.IsDeleted, &p.CreatedDate,
	)
	if err == sql.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, apperrors.NewInternalError("failed to query custom permission", err)
	}

	return &p, nil
}

// CreateCustomPermission creates a new custom permission
func (r *PermissionRepository) CreateCustomPermission(ctx context.Context, perm *models.SystemCustomPermission) error {
	const writerID = "nexuscrm-persistence/perm_custom.go#PermissionRepository.CreateCustomPermission"
	if r.permissionStateDirectWriterShouldRun(ctx, writerID) {
		return r.runPermissionStateDirectWriter(
			ctx,
			writerID,
			[]permissionStateDirectTarget{
				permissionStateDirectTargetFor(
					constants.TableCustomPermission,
					permissionStateDirectEqual(constants.FieldID),
					perm.ID,
				),
			},
			func(txCtx context.Context) error {
				return r.CreateCustomPermission(txCtx, perm)
			},
		)
	}
	qr := query.Insert(constants.TableCustomPermission, map[string]interface{}{
		constants.FieldID:                              perm.ID,
		constants.FieldSysCustomPermission_APIName:     perm.APIName,
		constants.FieldSysCustomPermission_Label:       perm.Label,
		constants.FieldSysCustomPermission_Description: perm.Description,
		constants.FieldSysCustomPermission_IsActive:    perm.IsActive,
		constants.FieldCreatedDate:                     query.Raw("NOW()"),
		constants.FieldLastModifiedDate:                query.Raw("NOW()"),
	}).Build()

	_, err := r.getExecutor(ctx).ExecContext(ctx, qr.SQL, qr.Params...)
	if err != nil {
		return apperrors.NewInternalError("failed to create custom permission", err)
	}

	return nil
}

// UpdateCustomPermission updates an existing custom permission
func (r *PermissionRepository) UpdateCustomPermission(ctx context.Context, perm *models.SystemCustomPermission) error {
	const writerID = "nexuscrm-persistence/perm_custom.go#PermissionRepository.UpdateCustomPermission"
	if r.permissionStateDirectWriterShouldRun(ctx, writerID) {
		return r.runPermissionStateDirectWriter(
			ctx,
			writerID,
			[]permissionStateDirectTarget{
				permissionStateDirectTargetFor(
					constants.TableCustomPermission,
					permissionStateDirectEqual(constants.FieldID),
					perm.ID,
				),
			},
			func(txCtx context.Context) error {
				return r.UpdateCustomPermission(txCtx, perm)
			},
		)
	}
	qr := query.Update(constants.TableCustomPermission).
		Set(map[string]interface{}{
			constants.FieldSysCustomPermission_APIName:     perm.APIName,
			constants.FieldSysCustomPermission_Label:       perm.Label,
			constants.FieldSysCustomPermission_Description: perm.Description,
			constants.FieldSysCustomPermission_IsActive:    perm.IsActive,
			constants.FieldLastModifiedDate:                query.Raw("NOW()"),
		}).
		Where(constants.FieldID, perm.ID).
		Build()

	_, err := r.getExecutor(ctx).ExecContext(ctx, qr.SQL, qr.Params...)
	if err != nil {
		return apperrors.NewInternalError("failed to update custom permission", err)
	}

	return nil
}

// DeleteCustomPermission deletes a custom permission and its references
func (r *PermissionRepository) DeleteCustomPermission(ctx context.Context, id string) error {
	const writerID = "nexuscrm-persistence/perm_custom.go#PermissionRepository.DeleteCustomPermission"
	if r.permissionStateDirectWriterShouldRun(ctx, writerID) {
		return r.runPermissionStateDirectWriter(
			ctx,
			writerID,
			customPermissionDeletePermissionStateTargets(id),
			func(txCtx context.Context) error {
				return r.DeleteCustomPermission(txCtx, id)
			},
		)
	}
	delProfileQr := query.Delete(constants.TableProfileCustomPermission).
		Where(constants.FieldProfileCustomPermission_CustomPermissionID, id).
		Build()
	if _, err := r.getExecutor(ctx).ExecContext(ctx, delProfileQr.SQL, delProfileQr.Params...); err != nil {
		return apperrors.NewInternalError("failed to delete profile custom permission refs", err)
	}

	delPermSetQr := query.Delete(constants.TablePermissionSetCustomPermission).
		Where(constants.FieldPermissionSetCustomPermission_CustomPermissionID, id).
		Build()
	if _, err := r.getExecutor(ctx).ExecContext(ctx, delPermSetQr.SQL, delPermSetQr.Params...); err != nil {
		return apperrors.NewInternalError("failed to delete permset custom permission refs", err)
	}

	qr := query.Delete(constants.TableCustomPermission).
		Where(constants.FieldID, id).
		Build()
	if _, err := r.getExecutor(ctx).ExecContext(ctx, qr.SQL, qr.Params...); err != nil {
		return apperrors.NewInternalError("failed to delete custom permission", err)
	}

	return nil
}

func customPermissionDeletePermissionStateTargets(id string) []permissionStateDirectTarget {
	return []permissionStateDirectTarget{
		permissionStateDirectTargetWithCustomPermissionCatalogPrelock(
			permissionStateDirectTargetWithManagedPermissionSetCurrentOwnerPrelock(
				permissionStateDirectTargetFor(
					constants.TablePermissionSetCustomPermission,
					permissionStateDirectEqual(constants.FieldPermissionSetCustomPermission_CustomPermissionID),
					id,
				),
				constants.FieldPermissionSetCustomPermission_PermissionSetID,
			),
			id,
		),
		permissionStateDirectTargetWithCustomPermissionCatalogPrelock(
			permissionStateDirectTargetFor(
				constants.TableProfileCustomPermission,
				permissionStateDirectEqual(constants.FieldProfileCustomPermission_CustomPermissionID),
				id,
			),
			id,
		),
		permissionStateDirectTargetFor(
			constants.TableCustomPermission,
			permissionStateDirectEqual(constants.FieldID),
			id,
		),
	}
}
