package issuereporting

import (
	"context"
	"encoding/json"
	"errors"

	dbgen "Yimaru-Backend/gen/db"
	"Yimaru-Backend/internal/domain"
	"Yimaru-Backend/internal/repository"
)

type Service struct {
	repo repository.ReportedIssueRepository
}

func New(repo repository.ReportedIssueRepository) *Service {
	return &Service{repo: repo}
}

func (s *Service) CreateReportedIssue(ctx context.Context, issue domain.ReportedIssueReq, userID int64, role domain.Role) (domain.ReportedIssue, error) {
	var metadata []byte
	if issue.Metadata != nil {
		var err error
		metadata, err = json.Marshal(issue.Metadata)
		if err != nil {
			return domain.ReportedIssue{}, err
		}
	}
	params := dbgen.CreateReportedIssueParams{
		UserID:      userID,
		UserRole:    string(role),
		Subject:     issue.Subject,
		Description: issue.Description,
		IssueType:   string(issue.IssueType),
		Metadata:    metadata,
	}
	dbIssue, err := s.repo.CreateReportedIssue(ctx, params)
	if err != nil {
		return domain.ReportedIssue{}, err
	}
	return mapDBIssueToDomain(dbIssue), nil
}

func (s *Service) GetIssuesForUser(ctx context.Context, userID int64, limit, offset int) ([]domain.ReportedIssue, error) {
	dbIssues, err := s.repo.ListReportedIssuesByUser(ctx, userID, int32(limit), int32(offset))
	if err != nil {
		return nil, err
	}
	return mapDBIssuesToDomain(dbIssues), nil
}

func (s *Service) GetAllIssues(ctx context.Context, limit, offset int) ([]domain.ReportedIssue, error) {
	dbIssues, err := s.repo.ListReportedIssues(ctx, int32(limit), int32(offset))
	if err != nil {
		return nil, err
	}
	return mapDBIssuesToDomain(dbIssues), nil
}

func (s *Service) GetIssueByID(ctx context.Context, issueID int64) (domain.ReportedIssue, error) {
	dbIssue, err := s.repo.GetReportedIssueByID(ctx, issueID)
	if err != nil {
		return domain.ReportedIssue{}, err
	}
	return mapDBIssueToDomain(dbIssue), nil
}

func (s *Service) UpdateIssueStatus(ctx context.Context, issueID int64, status string) error {
	validStatuses := map[string]bool{"pending": true, "in_progress": true, "resolved": true, "rejected": true}
	if !validStatuses[status] {
		return errors.New("invalid status")
	}
	return s.repo.UpdateReportedIssueStatus(ctx, issueID, status)
}

func (s *Service) CountAllIssues(ctx context.Context) (int64, error) {
	return s.repo.CountReportedIssues(ctx)
}

func (s *Service) CountIssuesByUser(ctx context.Context, userID int64) (int64, error) {
	return s.repo.CountReportedIssuesByUser(ctx, userID)
}

func (s *Service) DeleteIssue(ctx context.Context, issueID int64) error {
	return s.repo.DeleteReportedIssue(ctx, issueID)
}

func mapDBIssueToDomain(dbIssue dbgen.ReportedIssue) domain.ReportedIssue {
	issue := domain.ReportedIssue{
		ID:          dbIssue.ID,
		Subject:     dbIssue.Subject,
		Description: dbIssue.Description,
		UserID:      dbIssue.UserID,
		UserRole:    domain.Role(dbIssue.UserRole),
		Status:      domain.ReportedIssueStatus(dbIssue.Status),
		IssueType:   domain.ReportedIssueType(dbIssue.IssueType),
		CreatedAt:   dbIssue.CreatedAt.Time,
		UpdatedAt:   dbIssue.UpdatedAt.Time,
	}
	if len(dbIssue.Metadata) > 0 {
		_ = json.Unmarshal(dbIssue.Metadata, &issue.Metadata)
	}
	return issue
}

func mapDBIssuesToDomain(dbIssues []dbgen.ReportedIssue) []domain.ReportedIssue {
	issues := make([]domain.ReportedIssue, len(dbIssues))
	for i, dbIssue := range dbIssues {
		issues[i] = mapDBIssueToDomain(dbIssue)
	}
	return issues
}
