package services

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

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

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

const documentBucket = "kanzlai-documents"

type DocumentService struct {
	db      *sqlx.DB
	storage *StorageClient
	audit   *AuditService
}

func NewDocumentService(db *sqlx.DB, storage *StorageClient, audit *AuditService) *DocumentService {
	return &DocumentService{db: db, storage: storage, audit: audit}
}

type CreateDocumentInput struct {
	Title       string `json:"title"`
	DocType     string `json:"doc_type"`
	Filename    string
	ContentType string
	Size        int
	Data        io.Reader
}

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

func (s *DocumentService) GetByID(ctx context.Context, tenantID, docID uuid.UUID) (*models.Document, error) {
	var doc models.Document
	err := s.db.GetContext(ctx, &doc,
		"SELECT * FROM documents WHERE id = $1 AND tenant_id = $2", docID, tenantID)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, nil
		}
		return nil, fmt.Errorf("getting document: %w", err)
	}
	return &doc, nil
}

func (s *DocumentService) Create(ctx context.Context, tenantID, caseID, userID uuid.UUID, input CreateDocumentInput) (*models.Document, error) {
	// Verify case belongs to tenant
	var caseExists int
	if err := s.db.GetContext(ctx, &caseExists,
		"SELECT COUNT(*) FROM cases WHERE id = $1 AND tenant_id = $2",
		caseID, tenantID); err != nil {
		return nil, fmt.Errorf("verifying case: %w", err)
	}
	if caseExists == 0 {
		return nil, fmt.Errorf("case not found")
	}

	id := uuid.New()
	storagePath := fmt.Sprintf("%s/%s/%s_%s", tenantID, caseID, id, input.Filename)

	// Upload to Supabase Storage
	if err := s.storage.Upload(ctx, documentBucket, storagePath, input.ContentType, input.Data); err != nil {
		return nil, fmt.Errorf("uploading file: %w", err)
	}

	// Insert metadata record
	now := time.Now()
	_, err := s.db.ExecContext(ctx,
		`INSERT INTO documents (id, tenant_id, case_id, title, doc_type, file_path, file_size, mime_type, uploaded_by, created_at, updated_at)
		 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $10)`,
		id, tenantID, caseID, input.Title, nilIfEmpty(input.DocType), storagePath, input.Size, input.ContentType, userID, now)
	if err != nil {
		// Best effort: clean up uploaded file
		_ = s.storage.Delete(ctx, documentBucket, []string{storagePath})
		return nil, fmt.Errorf("inserting document record: %w", err)
	}

	// Log case event
	createEvent(ctx, s.db, tenantID, caseID, userID, "document_uploaded",
		fmt.Sprintf("Document uploaded: %s", input.Title), nil)

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

func (s *DocumentService) Download(ctx context.Context, tenantID, docID uuid.UUID) (io.ReadCloser, string, string, error) {
	doc, err := s.GetByID(ctx, tenantID, docID)
	if err != nil {
		return nil, "", "", err
	}
	if doc == nil {
		return nil, "", "", fmt.Errorf("document not found")
	}
	if doc.FilePath == nil {
		return nil, "", "", fmt.Errorf("document has no file")
	}

	body, contentType, err := s.storage.Download(ctx, documentBucket, *doc.FilePath)
	if err != nil {
		return nil, "", "", fmt.Errorf("downloading file: %w", err)
	}

	// Use stored mime_type if available, fall back to storage response
	if doc.MimeType != nil && *doc.MimeType != "" {
		contentType = *doc.MimeType
	}

	return body, contentType, doc.Title, nil
}

func (s *DocumentService) Delete(ctx context.Context, tenantID, docID, userID uuid.UUID) error {
	doc, err := s.GetByID(ctx, tenantID, docID)
	if err != nil {
		return err
	}
	if doc == nil {
		return sql.ErrNoRows
	}

	// Delete from storage
	if doc.FilePath != nil {
		if err := s.storage.Delete(ctx, documentBucket, []string{*doc.FilePath}); err != nil {
			return fmt.Errorf("deleting file from storage: %w", err)
		}
	}

	// Delete database record
	_, err = s.db.ExecContext(ctx,
		"DELETE FROM documents WHERE id = $1 AND tenant_id = $2", docID, tenantID)
	if err != nil {
		return fmt.Errorf("deleting document record: %w", err)
	}

	// Log case event
	createEvent(ctx, s.db, tenantID, doc.CaseID, userID, "document_deleted",
		fmt.Sprintf("Document deleted: %s", doc.Title), nil)
	s.audit.Log(ctx, "delete", "document", &docID, doc, nil)

	return nil
}

func nilIfEmpty(s string) *string {
	if s == "" {
		return nil
	}
	return &s
}
