package services

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"

	"mgit.msbls.de/m/KanzlAI-mGMT/internal/models"

	"github.com/google/uuid"
	"github.com/jmoiron/sqlx"
)

type PartyService struct {
	db    *sqlx.DB
	audit *AuditService
}

func NewPartyService(db *sqlx.DB, audit *AuditService) *PartyService {
	return &PartyService{db: db, audit: audit}
}

type CreatePartyInput struct {
	Name           string          `json:"name"`
	Role           *string         `json:"role,omitempty"`
	Representative *string         `json:"representative,omitempty"`
	ContactInfo    json.RawMessage `json:"contact_info,omitempty"`
}

type UpdatePartyInput struct {
	Name           *string         `json:"name,omitempty"`
	Role           *string         `json:"role,omitempty"`
	Representative *string         `json:"representative,omitempty"`
	ContactInfo    json.RawMessage `json:"contact_info,omitempty"`
}

func (s *PartyService) ListByCase(ctx context.Context, tenantID, caseID uuid.UUID) ([]models.Party, error) {
	var parties []models.Party
	err := s.db.SelectContext(ctx, &parties,
		"SELECT * FROM parties WHERE case_id = $1 AND tenant_id = $2 ORDER BY name",
		caseID, tenantID)
	if err != nil {
		return nil, fmt.Errorf("listing parties: %w", err)
	}
	return parties, nil
}

func (s *PartyService) Create(ctx context.Context, tenantID, caseID uuid.UUID, userID uuid.UUID, input CreatePartyInput) (*models.Party, error) {
	// Verify case exists and belongs to tenant
	var exists bool
	err := s.db.GetContext(ctx, &exists,
		"SELECT EXISTS(SELECT 1 FROM cases WHERE id = $1 AND tenant_id = $2)", caseID, tenantID)
	if err != nil {
		return nil, fmt.Errorf("checking case: %w", err)
	}
	if !exists {
		return nil, sql.ErrNoRows
	}

	id := uuid.New()
	contactInfo := input.ContactInfo
	if contactInfo == nil {
		contactInfo = json.RawMessage("{}")
	}

	_, err = s.db.ExecContext(ctx,
		`INSERT INTO parties (id, tenant_id, case_id, name, role, representative, contact_info)
		 VALUES ($1, $2, $3, $4, $5, $6, $7)`,
		id, tenantID, caseID, input.Name, input.Role, input.Representative, contactInfo)
	if err != nil {
		return nil, fmt.Errorf("creating party: %w", err)
	}

	// Log event
	desc := fmt.Sprintf("Party added: %s", input.Name)
	createEvent(ctx, s.db, tenantID, caseID, userID, "party_added", desc, nil)

	var party models.Party
	if err := s.db.GetContext(ctx, &party, "SELECT * FROM parties WHERE id = $1", id); err != nil {
		return nil, fmt.Errorf("fetching created party: %w", err)
	}
	s.audit.Log(ctx, "create", "party", &id, nil, party)
	return &party, nil
}

func (s *PartyService) Update(ctx context.Context, tenantID, partyID uuid.UUID, input UpdatePartyInput) (*models.Party, error) {
	// Verify party exists and belongs to tenant
	var current models.Party
	err := s.db.GetContext(ctx, &current,
		"SELECT * FROM parties WHERE id = $1 AND tenant_id = $2", partyID, tenantID)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, nil
		}
		return nil, fmt.Errorf("fetching party: %w", err)
	}

	sets := []string{}
	args := []interface{}{}
	argIdx := 1

	if input.Name != nil {
		sets = append(sets, fmt.Sprintf("name = $%d", argIdx))
		args = append(args, *input.Name)
		argIdx++
	}
	if input.Role != nil {
		sets = append(sets, fmt.Sprintf("role = $%d", argIdx))
		args = append(args, *input.Role)
		argIdx++
	}
	if input.Representative != nil {
		sets = append(sets, fmt.Sprintf("representative = $%d", argIdx))
		args = append(args, *input.Representative)
		argIdx++
	}
	if input.ContactInfo != nil {
		sets = append(sets, fmt.Sprintf("contact_info = $%d", argIdx))
		args = append(args, input.ContactInfo)
		argIdx++
	}

	if len(sets) == 0 {
		return &current, nil
	}

	query := fmt.Sprintf("UPDATE parties SET %s WHERE id = $%d AND tenant_id = $%d",
		joinStrings(sets, ", "), argIdx, argIdx+1)
	args = append(args, partyID, tenantID)

	if _, err := s.db.ExecContext(ctx, query, args...); err != nil {
		return nil, fmt.Errorf("updating party: %w", err)
	}

	var updated models.Party
	if err := s.db.GetContext(ctx, &updated, "SELECT * FROM parties WHERE id = $1", partyID); err != nil {
		return nil, fmt.Errorf("fetching updated party: %w", err)
	}
	s.audit.Log(ctx, "update", "party", &partyID, current, updated)
	return &updated, nil
}

func (s *PartyService) Delete(ctx context.Context, tenantID, partyID uuid.UUID) error {
	result, err := s.db.ExecContext(ctx,
		"DELETE FROM parties WHERE id = $1 AND tenant_id = $2", partyID, tenantID)
	if err != nil {
		return fmt.Errorf("deleting party: %w", err)
	}
	rows, _ := result.RowsAffected()
	if rows == 0 {
		return sql.ErrNoRows
	}
	s.audit.Log(ctx, "delete", "party", &partyID, nil, nil)
	return nil
}
