package repository

import (
	dbgen "Yimaru-Backend/gen/db"
	"Yimaru-Backend/internal/domain"
	"context"
	"encoding/json"

	"github.com/jackc/pgx/v5/pgtype"
)

func (s *Store) CreateActivityLog(ctx context.Context, log domain.ActivityLog) (domain.ActivityLog, error) {
	var actorID, resourceID pgtype.Int8
	var actorRole, message, ipAddress, userAgent pgtype.Text

	if log.ActorID != nil {
		actorID = pgtype.Int8{Int64: *log.ActorID, Valid: true}
	}
	if log.ResourceID != nil {
		resourceID = pgtype.Int8{Int64: *log.ResourceID, Valid: true}
	}
	if log.ActorRole != nil {
		actorRole = pgtype.Text{String: *log.ActorRole, Valid: true}
	}
	if log.Message != nil {
		message = pgtype.Text{String: *log.Message, Valid: true}
	}
	if log.IPAddress != nil {
		ipAddress = pgtype.Text{String: *log.IPAddress, Valid: true}
	}
	if log.UserAgent != nil {
		userAgent = pgtype.Text{String: *log.UserAgent, Valid: true}
	}

	var metadata interface{}
	if log.Metadata != nil {
		metadata = log.Metadata
	}

	row, err := s.queries.CreateActivityLog(ctx, dbgen.CreateActivityLogParams{
		ActorID:      actorID,
		ActorRole:    actorRole,
		Action:       log.Action,
		ResourceType: log.ResourceType,
		ResourceID:   resourceID,
		Message:      message,
		Metadata:     metadata,
		IpAddress:    ipAddress,
		UserAgent:    userAgent,
	})
	if err != nil {
		return domain.ActivityLog{}, err
	}

	return mapActivityLogRow(row), nil
}

func (s *Store) ListActivityLogs(ctx context.Context, filter domain.ActivityLogFilter) ([]domain.ActivityLog, int64, error) {
	var filterActorID, filterResourceID pgtype.Int8
	var filterAction, filterResourceType pgtype.Text
	var filterAfter, filterBefore pgtype.Timestamptz

	if filter.ActorID != nil {
		filterActorID = pgtype.Int8{Int64: *filter.ActorID, Valid: true}
	}
	if filter.ResourceID != nil {
		filterResourceID = pgtype.Int8{Int64: *filter.ResourceID, Valid: true}
	}
	if filter.Action != nil {
		filterAction = pgtype.Text{String: *filter.Action, Valid: true}
	}
	if filter.ResourceType != nil {
		filterResourceType = pgtype.Text{String: *filter.ResourceType, Valid: true}
	}
	if filter.After != nil {
		filterAfter = pgtype.Timestamptz{Time: *filter.After, Valid: true}
	}
	if filter.Before != nil {
		filterBefore = pgtype.Timestamptz{Time: *filter.Before, Valid: true}
	}

	rows, err := s.queries.ListActivityLogs(ctx, dbgen.ListActivityLogsParams{
		FilterActorID:      filterActorID,
		FilterAction:       filterAction,
		FilterResourceType: filterResourceType,
		FilterResourceID:   filterResourceID,
		FilterAfter:        filterAfter,
		FilterBefore:       filterBefore,
		LogOffset:          filter.Offset,
		LogLimit:           filter.Limit,
	})
	if err != nil {
		return nil, 0, err
	}

	var (
		logs       []domain.ActivityLog
		totalCount int64
	)

	for i, row := range rows {
		if i == 0 {
			totalCount = row.TotalCount
		}

		logs = append(logs, domain.ActivityLog{
			ID:           row.ID,
			ActorID:      ptrInt64(row.ActorID),
			ActorRole:    ptrString(row.ActorRole),
			Action:       row.Action,
			ResourceType: row.ResourceType,
			ResourceID:   ptrInt64(row.ResourceID),
			Message:      ptrString(row.Message),
			Metadata:     json.RawMessage(row.Metadata),
			IPAddress:    ptrString(row.IpAddress),
			UserAgent:    ptrString(row.UserAgent),
			CreatedAt:    row.CreatedAt.Time,
		})
	}

	return logs, totalCount, nil
}

func (s *Store) GetActivityLogByID(ctx context.Context, id int64) (domain.ActivityLog, error) {
	row, err := s.queries.GetActivityLogByID(ctx, id)
	if err != nil {
		return domain.ActivityLog{}, err
	}

	return mapActivityLogRow(row), nil
}

func mapActivityLogRow(row dbgen.ActivityLog) domain.ActivityLog {
	return domain.ActivityLog{
		ID:           row.ID,
		ActorID:      ptrInt64(row.ActorID),
		ActorRole:    ptrString(row.ActorRole),
		Action:       row.Action,
		ResourceType: row.ResourceType,
		ResourceID:   ptrInt64(row.ResourceID),
		Message:      ptrString(row.Message),
		Metadata:     json.RawMessage(row.Metadata),
		IPAddress:    ptrString(row.IpAddress),
		UserAgent:    ptrString(row.UserAgent),
		CreatedAt:    row.CreatedAt.Time,
	}
}
