package db

import (
	"database/sql"
	"encoding/json"
	"errors"
	"os"
	"path/filepath"
)

func (s *Store) InsertFeedback(item Feedback) error {
	now := Now()
	if item.Code == "" {
		item.Code = NewFeedbackCode()
	}
	if item.Status == "" {
		item.Status = "new"
	}
	if item.Category == "" {
		item.Category = normalizeCategory(item.Type)
	}
	if item.Priority == "" {
		item.Priority = normalizePriority(item.Severity)
	}
	if item.SLALevel == "" {
		item.SLALevel = defaultSLA(item.Priority)
	}
	if item.SourceChannel == "" {
		item.SourceChannel = "winui"
	}
	if item.RiskScore == 0 {
		item.RiskScore = defaultRisk(item.Priority)
	}
	if item.StatusDetail == "" {
		item.StatusDetail = "反馈已接收，等待后台处理。"
	}
	if item.CreatedAt == "" {
		item.CreatedAt = now
	}
	item.UpdatedAt = now
	item.LastActivityAt = now
	tagsJSON, _ := json.Marshal(normalizeTags(item.Tags))
	_, err := s.exec(`INSERT INTO feedback_tickets (
		code, title, type, severity, category, priority, contact, body, status, status_detail,
		public_reply, note, assignee, handled_by, due_at, resolved_at, archived_at, sla_level,
		source_channel, risk_score, resolution, attachment, package_path, encrypted_package_path,
		package_sha256, plain_package_sha256, summary_text, included_files, mail_sent, remote_addr,
		tags, created_at, updated_at, last_activity_at
	) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
		item.Code, sanitize(item.Title), sanitize(item.Type), sanitize(item.Severity), item.Category, item.Priority,
		sanitize(item.Contact), sanitizeLong(item.Body, 5000), item.Status, sanitize(item.StatusDetail),
		sanitizeLong(item.PublicReply, 3000), sanitizeLong(item.Note, 3000), sanitize(item.Assignee), sanitize(item.HandledBy),
		item.DueAt, item.ResolvedAt, item.ArchivedAt, item.SLALevel, item.SourceChannel, item.RiskScore,
		sanitizeLong(item.Resolution, 3000), item.Attachment, item.PackagePath, item.EncryptedPackagePath,
		item.PackageSha256, item.PlainPackageSha256, sanitizeLong(item.SummaryText, 6000), item.IncludedFiles,
		boolInt(item.MailSent), sanitize(item.RemoteAddr), string(tagsJSON), item.CreatedAt, item.UpdatedAt, item.LastActivityAt)
	if err != nil {
		return err
	}
	if item.PackagePath != "" {
		_ = s.InsertFeedbackAttachment(FeedbackAttachment{FeedbackCode: item.Code, Kind: "package", Path: item.PackagePath, FileName: filepath.Base(item.PackagePath), SHA256: item.PlainPackageSha256})
	}
	if item.EncryptedPackagePath != "" {
		_ = s.InsertFeedbackAttachment(FeedbackAttachment{FeedbackCode: item.Code, Kind: "encrypted_package", Path: item.EncryptedPackagePath, FileName: filepath.Base(item.EncryptedPackagePath), SHA256: item.PackageSha256})
	}
	return nil
}

func (s *Store) GetFeedback(code string) (Feedback, error) {
	item, err := s.scanFeedbackRow(s.queryRow(feedbackSelectSQL()+` WHERE code = ?`, code))
	if errors.Is(err, sql.ErrNoRows) {
		return Feedback{}, errors.New("feedback not found")
	}
	return item, err
}

func (s *Store) GetFeedbackDetail(code string) (*FeedbackDetail, error) {
	item, err := s.GetFeedback(code)
	if err != nil {
		return nil, err
	}
	comments, err := s.ListFeedbackComments(code)
	if err != nil {
		return nil, err
	}
	attachments, err := s.ListFeedbackAttachments(code)
	if err != nil {
		return nil, err
	}
	events, _ := s.ListAuditLogsForTarget(code, 100)
	legacyEvents, _ := s.ListFeedbackEvents(code, 100)
	mailRecords, _ := s.ListMailRecords(code, 100)
	return &FeedbackDetail{Feedback: item, Comments: comments, Attachments: attachments, Events: events, LegacyEvents: legacyEvents, MailRecords: mailRecords}, nil
}

func (s *Store) ListFeedbacks(limit int) ([]Feedback, error) {
	if limit <= 0 || limit > 200 {
		limit = 50
	}
	rows, err := s.query(feedbackSelectSQL()+` ORDER BY last_activity_at DESC, created_at DESC LIMIT ?`, limit)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	return scanFeedbackRows(rows)
}

func (s *Store) ListFeedbacksFiltered(page, perPage int, filters FeedbackFilters) ([]Feedback, int, error) {
	page, perPage = normalizePage(page, perPage)
	where, args := feedbackWhere(filters)
	var total int
	if err := s.queryRow(`SELECT COUNT(*) FROM feedback_tickets`+where, args...).Scan(&total); err != nil {
		return nil, 0, err
	}
	order := ` ORDER BY last_activity_at DESC, created_at DESC`
	if filters.Sort == "oldest" {
		order = ` ORDER BY created_at ASC`
	}
	args = append(args, perPage, (page-1)*perPage)
	rows, err := s.query(feedbackSelectSQL()+where+order+` LIMIT ? OFFSET ?`, args...)
	if err != nil {
		return nil, 0, err
	}
	defer rows.Close()
	items, err := scanFeedbackRows(rows)
	return items, total, err
}

func (s *Store) UpdateFeedback(code, status, detail, reply string) error {
	update := FeedbackUpdate{Status: status, StatusDetail: detail, PublicReply: reply, Actor: "admin"}
	return s.UpdateFeedbackTicket(code, update)
}

func (s *Store) UpdateFeedbackTicket(code string, update FeedbackUpdate) error {
	current, err := s.GetFeedback(code)
	if err != nil {
		return err
	}
	if update.Status == "" {
		update.Status = current.Status
	}
	if update.Category == "" {
		update.Category = current.Category
	}
	if update.Priority == "" {
		update.Priority = current.Priority
	}
	if update.SLALevel == "" {
		update.SLALevel = current.SLALevel
	}
	if update.StatusDetail == "" {
		update.StatusDetail = current.StatusDetail
	}
	if update.PublicReply == "" {
		update.PublicReply = current.PublicReply
	}
	if update.Note == "" {
		update.Note = current.Note
	}
	if update.Assignee == "" {
		update.Assignee = current.Assignee
	}
	if update.HandledBy == "" {
		update.HandledBy = current.HandledBy
	}
	if update.DueAt == "" {
		update.DueAt = current.DueAt
	}
	if update.Resolution == "" {
		update.Resolution = current.Resolution
	}
	tags := current.Tags
	if len(update.Tags) > 0 {
		tags = update.Tags
	}
	tagsJSON, _ := json.Marshal(normalizeTags(tags))
	now := Now()
	_, err = s.exec(`UPDATE feedback_tickets SET status = ?, category = ?, priority = ?, status_detail = ?, public_reply = ?,
		note = ?, assignee = ?, handled_by = ?, due_at = ?, sla_level = ?, resolution = ?, tags = ?, updated_at = ?, last_activity_at = ?
		WHERE code = ?`,
		update.Status, update.Category, update.Priority, sanitize(update.StatusDetail), sanitizeLong(update.PublicReply, 3000),
		sanitizeLong(update.Note, 3000), sanitize(update.Assignee), sanitize(update.HandledBy), update.DueAt, update.SLALevel,
		sanitizeLong(update.Resolution, 3000), string(tagsJSON), now, now, code)
	if err == nil {
		_ = s.InsertAudit(AuditLog{Actor: firstNonEmpty(update.Actor, "admin"), Type: "feedback.updated", Target: code, Message: "反馈工单已更新"})
	}
	return err
}

func (s *Store) BulkUpdateFeedback(codes []string, update FeedbackUpdate) error {
	for _, code := range codes {
		if err := s.UpdateFeedbackTicket(code, update); err != nil {
			return err
		}
	}
	return nil
}

func (s *Store) InsertFeedbackComment(comment FeedbackComment) (FeedbackComment, error) {
	if comment.CreatedAt == "" {
		comment.CreatedAt = Now()
	}
	id, err := s.insertID(`INSERT INTO feedback_comments (feedback_code, author, body, internal, created_at) VALUES (?, ?, ?, ?, ?)`,
		comment.Code, sanitize(comment.Author), sanitizeLong(comment.Body, 3000), boolInt(comment.Internal), comment.CreatedAt)
	if err != nil {
		return FeedbackComment{}, err
	}
	comment.ID = id
	return comment, nil
}

func (s *Store) ListFeedbackComments(code string) ([]FeedbackComment, error) {
	rows, err := s.query(`SELECT id, feedback_code, author, body, internal, created_at FROM feedback_comments WHERE feedback_code = ? ORDER BY id ASC`, code)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	items := []FeedbackComment{}
	for rows.Next() {
		var item FeedbackComment
		var internal int
		if err := rows.Scan(&item.ID, &item.Code, &item.Author, &item.Body, &internal, &item.CreatedAt); err != nil {
			return nil, err
		}
		item.Internal = internal == 1
		items = append(items, item)
	}
	return items, rows.Err()
}

func (s *Store) InsertFeedbackAttachment(item FeedbackAttachment) error {
	if item.CreatedAt == "" {
		item.CreatedAt = Now()
	}
	if item.FileName == "" {
		item.FileName = filepath.Base(item.Path)
	}
	if item.SizeBytes == 0 {
		if info, err := os.Stat(item.Path); err == nil {
			item.SizeBytes = info.Size()
		}
	}
	_, err := s.exec(`INSERT INTO feedback_attachments (feedback_code, kind, path, file_name, sha256, size_bytes, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)`,
		item.FeedbackCode, item.Kind, item.Path, item.FileName, item.SHA256, item.SizeBytes, item.CreatedAt)
	return err
}

func (s *Store) ListFeedbackAttachments(code string) ([]FeedbackAttachment, error) {
	rows, err := s.query(`SELECT id, feedback_code, kind, path, file_name, sha256, size_bytes, created_at FROM feedback_attachments WHERE feedback_code = ? ORDER BY id ASC`, code)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	items := []FeedbackAttachment{}
	for rows.Next() {
		var item FeedbackAttachment
		if err := rows.Scan(&item.ID, &item.FeedbackCode, &item.Kind, &item.Path, &item.FileName, &item.SHA256, &item.SizeBytes, &item.CreatedAt); err != nil {
			return nil, err
		}
		items = append(items, item)
	}
	return items, rows.Err()
}

func (s *Store) UpsertFeedbackEvent(item LegacyFeedbackEvent) error {
	if item.CreatedAt == "" {
		item.CreatedAt = Now()
	}
	conn, d := s.active()
	columns := []string{"id", "feedback_code", "event_type", "actor", "from_value", "to_value", "message", "created_at"}
	_, err := conn.Exec(d.rebind(d.upsert("feedback_events", columns, []string{"id"})),
		item.ID, sanitize(item.FeedbackCode), sanitize(item.EventType), sanitize(item.Actor), sanitize(item.FromValue), sanitize(item.ToValue), sanitizeLong(item.Message, 1000), item.CreatedAt)
	if err != nil {
		s.markFailover(err)
	}
	return err
}

func (s *Store) UpsertFeedbackTag(code, tag, createdAt string) error {
	if createdAt == "" {
		createdAt = Now()
	}
	conn, d := s.active()
	columns := []string{"feedback_code", "tag", "created_at"}
	_, err := conn.Exec(d.rebind(d.upsert("feedback_tags", columns, []string{"feedback_code", "tag"})), sanitize(code), sanitize(tag), createdAt)
	if err != nil {
		s.markFailover(err)
	}
	return err
}

func (s *Store) UpsertMailRecord(item LegacyMailRecord) error {
	if item.CreatedAt == "" {
		item.CreatedAt = Now()
	}
	conn, d := s.active()
	columns := []string{"id", "feedback_code", "kind", "status", "to_address", "subject", "plain_body", "html_body", "attachment_path", "attachment_name", "error_message", "created_at", "sent_at"}
	_, err := conn.Exec(d.rebind(d.upsert("mail_records", columns, []string{"id"})),
		item.ID, sanitize(item.FeedbackCode), sanitize(item.Kind), sanitize(item.Status), sanitize(item.ToAddress), sanitizeLong(item.Subject, 1000),
		"", "", item.AttachmentPath, item.AttachmentName, sanitizeLong(item.ErrorMessage, 1000), item.CreatedAt, item.SentAt)
	if err != nil {
		s.markFailover(err)
	}
	return err
}

func (s *Store) InsertMailRecord(item LegacyMailRecord) (int64, error) {
	if item.CreatedAt == "" {
		item.CreatedAt = Now()
	}
	id, err := s.insertID(`INSERT INTO mail_records (
		feedback_code, kind, status, to_address, subject, plain_body, html_body,
		attachment_path, attachment_name, error_message, created_at, sent_at
	) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
		sanitize(item.FeedbackCode), sanitize(firstNonEmpty(item.Kind, "feedback")), sanitize(firstNonEmpty(item.Status, "pending")),
		sanitize(item.ToAddress), sanitizeLong(item.Subject, 1000), sanitizeLong(item.PlainBody, 12000), sanitizeLong(item.HTMLBody, 12000),
		item.AttachmentPath, item.AttachmentName, sanitizeLong(item.ErrorMessage, 1000), item.CreatedAt, item.SentAt)
	return id, err
}

func (s *Store) UpdateMailState(id int64, status, errorMessage string) error {
	sentAt := ""
	if status == "sent" {
		sentAt = Now()
	}
	_, err := s.exec(`UPDATE mail_records SET status = ?, error_message = ?, sent_at = ? WHERE id = ?`,
		sanitize(status), sanitizeLong(errorMessage, 1000), sentAt, id)
	return err
}

func (s *Store) UpdateFeedbackMailState(code string, sent bool) error {
	_, err := s.exec(`UPDATE feedback_tickets SET mail_sent = ?, updated_at = ?, last_activity_at = ? WHERE code = ?`,
		boolInt(sent), Now(), Now(), code)
	return err
}

func (s *Store) ListFeedbackEvents(code string, limit int) ([]LegacyFeedbackEvent, error) {
	if limit <= 0 || limit > 200 {
		limit = 100
	}
	rows, err := s.query(`SELECT id, feedback_code, event_type, actor, from_value, to_value, message, created_at FROM feedback_events WHERE feedback_code = ? ORDER BY created_at DESC, id DESC LIMIT ?`, code, limit)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	items := []LegacyFeedbackEvent{}
	for rows.Next() {
		var item LegacyFeedbackEvent
		if err := rows.Scan(&item.ID, &item.FeedbackCode, &item.EventType, &item.Actor, &item.FromValue, &item.ToValue, &item.Message, &item.CreatedAt); err != nil {
			return nil, err
		}
		items = append(items, item)
	}
	return items, rows.Err()
}

func (s *Store) ListMailRecords(code string, limit int) ([]LegacyMailRecord, error) {
	if limit <= 0 || limit > 200 {
		limit = 100
	}
	rows, err := s.query(`SELECT id, feedback_code, kind, status, to_address, subject, attachment_path, attachment_name, error_message, created_at, sent_at FROM mail_records WHERE feedback_code = ? ORDER BY created_at DESC, id DESC LIMIT ?`, code, limit)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	items := []LegacyMailRecord{}
	for rows.Next() {
		var item LegacyMailRecord
		if err := rows.Scan(&item.ID, &item.FeedbackCode, &item.Kind, &item.Status, &item.ToAddress, &item.Subject, &item.AttachmentPath, &item.AttachmentName, &item.ErrorMessage, &item.CreatedAt, &item.SentAt); err != nil {
			return nil, err
		}
		items = append(items, item)
	}
	return items, rows.Err()
}
