package reporting

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

	"github.com/tidbcloud/AgentTeam/internal/core/identifiers"
)

const (
	StatusOpen = "open"

	contentReportObjectType   = "content-reports"
	contentReportSubmitLimit  = 5
	contentReportSubmitWindow = time.Minute
)

type ContentReport struct {
	ID                 string
	TargetURL          string
	Reason             string
	Description        string
	Contact            string
	ReporterUserID     string
	ReporterIP         string
	UserAgent          string
	ImageFilename      string
	ImageContentType   string
	ImageSizeBytes     int64
	ImageStorageDriver string
	ImageObjectKey     string
	Status             string
	Note               string
	CreatedAt          time.Time
	UpdatedAt          time.Time
}

type CreateContentReportParams struct {
	TargetURL      string
	Reason         string
	Description    string
	Contact        string
	ReporterUserID string
	ReporterIP     string
	UserAgent      string
	Image          *ContentReportImageUpload
}

type ContentReportImageUpload struct {
	Filename    string
	ContentType string
	Blob        []byte
}

func (store *Store) CreateContentReport(ctx context.Context, params CreateContentReportParams) (ContentReport, error) {
	if store == nil || store.db == nil {
		return ContentReport{}, fmt.Errorf("reporting store is not configured")
	}

	normalized, err := normalizeCreateContentReportParams(params)
	if err != nil {
		return ContentReport{}, err
	}

	reportID, err := identifiers.NewEntityID("rpt")
	if err != nil {
		return ContentReport{}, fmt.Errorf("generate content report id: %w", err)
	}

	now := time.Now().UTC()
	report := ContentReport{
		ID:             reportID,
		TargetURL:      normalized.TargetURL,
		Reason:         normalized.Reason,
		Description:    normalized.Description,
		Contact:        normalized.Contact,
		ReporterUserID: normalized.ReporterUserID,
		ReporterIP:     normalized.ReporterIP,
		UserAgent:      normalized.UserAgent,
		Status:         StatusOpen,
		CreatedAt:      now,
		UpdatedAt:      now,
	}
	if err := store.requireContentReportSubmitRateLimit(ctx, report.ReporterUserID, now); err != nil {
		return ContentReport{}, err
	}

	var imagePayload []byte
	if normalized.Image != nil {
		report.ImageFilename = strings.TrimSpace(normalized.Image.Filename)
		report.ImageContentType = strings.TrimSpace(normalized.Image.ContentType)
		report.ImageSizeBytes = int64(len(normalized.Image.Blob))
		if store.mediaObjectStorage != nil {
			report.ImageStorageDriver = store.mediaObjectStorage.Driver()
			report.ImageObjectKey = contentReportObjectKey(report.ID)
			if err := store.mediaObjectStorage.Put(ctx, report.ImageObjectKey, report.ImageContentType, normalized.Image.Blob); err != nil {
				return ContentReport{}, fmt.Errorf("put content report image: %w", err)
			}
		} else {
			imagePayload = normalized.Image.Blob
		}
	}

	if _, err := store.db.Exec(ctx, `
INSERT INTO content_reports (
  id,
  target_url,
  reason,
  description,
  contact,
  reporter_user_id,
  reporter_ip,
  user_agent,
  image_filename,
  image_content_type,
  image_size_bytes,
  image_storage_driver,
  image_object_key,
  image_payload,
  status,
  note,
  created_at,
  updated_at
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, NULL, $16, $17)
`, report.ID,
		report.TargetURL,
		report.Reason,
		report.Description,
		report.Contact,
		report.ReporterUserID,
		report.ReporterIP,
		report.UserAgent,
		report.ImageFilename,
		report.ImageContentType,
		report.ImageSizeBytes,
		report.ImageStorageDriver,
		nullableString(report.ImageObjectKey),
		imagePayload,
		report.Status,
		report.CreatedAt,
		report.UpdatedAt,
	); err != nil {
		store.deleteContentReportImageBestEffort(ctx, report.ImageStorageDriver, report.ImageObjectKey)
		return ContentReport{}, fmt.Errorf("insert content report: %w", err)
	}

	return report, nil
}

func normalizeCreateContentReportParams(params CreateContentReportParams) (CreateContentReportParams, error) {
	normalized := CreateContentReportParams{
		TargetURL:      truncateString(strings.TrimSpace(params.TargetURL), 2048),
		Reason:         strings.ToLower(strings.TrimSpace(params.Reason)),
		Description:    truncateString(strings.TrimSpace(params.Description), 4000),
		Contact:        truncateString(strings.TrimSpace(params.Contact), 255),
		ReporterUserID: truncateString(strings.TrimSpace(params.ReporterUserID), 255),
		ReporterIP:     truncateString(strings.TrimSpace(params.ReporterIP), 128),
		UserAgent:      truncateString(strings.TrimSpace(params.UserAgent), 512),
		Image:          params.Image,
	}
	if normalized.TargetURL == "" {
		return CreateContentReportParams{}, badRequest("targetUrl is required")
	}
	if normalized.Reason == "" {
		return CreateContentReportParams{}, badRequest("reason is required")
	}
	if normalized.Description == "" {
		return CreateContentReportParams{}, badRequest("description is required")
	}
	if normalized.ReporterUserID == "" {
		return CreateContentReportParams{}, badRequest("reporterUserId is required")
	}
	if !isAllowedContentReportReason(normalized.Reason) {
		return CreateContentReportParams{}, badRequest("reason is invalid")
	}
	if normalized.Image != nil {
		if strings.TrimSpace(normalized.Image.Filename) == "" {
			normalized.Image.Filename = "report-image"
		}
		if strings.TrimSpace(normalized.Image.ContentType) == "" {
			return CreateContentReportParams{}, badRequest("image content type is required")
		}
		if len(normalized.Image.Blob) == 0 {
			return CreateContentReportParams{}, badRequest("image is empty")
		}
	}
	return normalized, nil
}

func isAllowedContentReportReason(reason string) bool {
	switch strings.ToLower(strings.TrimSpace(reason)) {
	case "spam", "harassment", "illegal", "privacy", "copyright", "other":
		return true
	default:
		return false
	}
}

func (store *Store) requireContentReportSubmitRateLimit(ctx context.Context, reporterUserID string, now time.Time) error {
	var recentCount int
	if err := store.db.QueryRow(ctx, `
SELECT COUNT(*)
FROM content_reports
WHERE reporter_user_id = $1
  AND created_at >= $2
`, reporterUserID, now.Add(-contentReportSubmitWindow)).Scan(&recentCount); err != nil {
		return fmt.Errorf("count recent content reports: %w", err)
	}
	if recentCount >= contentReportSubmitLimit {
		return fmt.Errorf("%w: content report submit limit exceeded", ErrRateLimited)
	}
	return nil
}

func contentReportObjectKey(reportID string) string {
	return contentReportObjectType + "/" + strings.TrimSpace(reportID)
}

func (store *Store) deleteContentReportImageBestEffort(ctx context.Context, storageDriver string, objectKey string) {
	if store == nil || store.mediaObjectStorage == nil {
		return
	}
	if strings.TrimSpace(storageDriver) == "" || strings.TrimSpace(objectKey) == "" {
		return
	}
	_ = store.mediaObjectStorage.Delete(ctx, objectKey)
}

func nullableString(value string) sql.NullString {
	trimmed := strings.TrimSpace(value)
	return sql.NullString{String: trimmed, Valid: trimmed != ""}
}

func truncateString(value string, limit int) string {
	if limit <= 0 || len(value) <= limit {
		return value
	}
	return value[:limit]
}
