package services

import (
	"context"
	"database/sql"
	"fmt"
	"time"

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

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

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

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

// ListByParent returns all notes for a given parent entity, scoped to tenant.
func (s *NoteService) ListByParent(ctx context.Context, tenantID uuid.UUID, parentType string, parentID uuid.UUID) ([]models.Note, error) {
	col, err := parentColumn(parentType)
	if err != nil {
		return nil, err
	}

	query := fmt.Sprintf(
		`SELECT id, tenant_id, case_id, deadline_id, appointment_id, case_event_id,
		        content, created_by, created_at, updated_at
		 FROM notes
		 WHERE tenant_id = $1 AND %s = $2
		 ORDER BY created_at DESC`, col)

	var notes []models.Note
	if err := s.db.SelectContext(ctx, &notes, query, tenantID, parentID); err != nil {
		return nil, fmt.Errorf("listing notes by %s: %w", parentType, err)
	}
	if notes == nil {
		notes = []models.Note{}
	}
	return notes, nil
}

type CreateNoteInput struct {
	CaseID        *uuid.UUID `json:"case_id,omitempty"`
	DeadlineID    *uuid.UUID `json:"deadline_id,omitempty"`
	AppointmentID *uuid.UUID `json:"appointment_id,omitempty"`
	CaseEventID   *uuid.UUID `json:"case_event_id,omitempty"`
	Content       string     `json:"content"`
}

// Create inserts a new note.
func (s *NoteService) Create(ctx context.Context, tenantID uuid.UUID, createdBy *uuid.UUID, input CreateNoteInput) (*models.Note, error) {
	id := uuid.New()
	now := time.Now().UTC()

	query := `INSERT INTO notes (id, tenant_id, case_id, deadline_id, appointment_id, case_event_id, content, created_by, created_at, updated_at)
	          VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $9)
	          RETURNING id, tenant_id, case_id, deadline_id, appointment_id, case_event_id, content, created_by, created_at, updated_at`

	var n models.Note
	err := s.db.GetContext(ctx, &n, query,
		id, tenantID, input.CaseID, input.DeadlineID, input.AppointmentID, input.CaseEventID,
		input.Content, createdBy, now)
	if err != nil {
		return nil, fmt.Errorf("creating note: %w", err)
	}
	s.audit.Log(ctx, "create", "note", &id, nil, n)
	return &n, nil
}

// Update modifies a note's content.
func (s *NoteService) Update(ctx context.Context, tenantID, noteID uuid.UUID, content string) (*models.Note, error) {
	query := `UPDATE notes SET content = $1, updated_at = $2
	          WHERE id = $3 AND tenant_id = $4
	          RETURNING id, tenant_id, case_id, deadline_id, appointment_id, case_event_id, content, created_by, created_at, updated_at`

	var n models.Note
	err := s.db.GetContext(ctx, &n, query, content, time.Now().UTC(), noteID, tenantID)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, nil
		}
		return nil, fmt.Errorf("updating note: %w", err)
	}
	s.audit.Log(ctx, "update", "note", &noteID, nil, n)
	return &n, nil
}

// Delete removes a note.
func (s *NoteService) Delete(ctx context.Context, tenantID, noteID uuid.UUID) error {
	result, err := s.db.ExecContext(ctx, "DELETE FROM notes WHERE id = $1 AND tenant_id = $2", noteID, tenantID)
	if err != nil {
		return fmt.Errorf("deleting note: %w", err)
	}
	rows, err := result.RowsAffected()
	if err != nil {
		return fmt.Errorf("checking delete result: %w", err)
	}
	if rows == 0 {
		return fmt.Errorf("note not found")
	}
	s.audit.Log(ctx, "delete", "note", &noteID, nil, nil)
	return nil
}

func parentColumn(parentType string) (string, error) {
	switch parentType {
	case "case":
		return "case_id", nil
	case "deadline":
		return "deadline_id", nil
	case "appointment":
		return "appointment_id", nil
	case "case_event":
		return "case_event_id", nil
	default:
		return "", fmt.Errorf("invalid parent type: %s", parentType)
	}
}
