package httpapi

import (
	"context"
	"errors"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/easyai/easyai-ai-gateway/apps/api/internal/config"
	"github.com/easyai/easyai-ai-gateway/apps/api/internal/store"
)

func (s *Server) startLocalTempAssetCleanup(ctx context.Context) {
	go func() {
		s.cleanupExpiredLocalTempAssets(ctx, time.Now())
		ticker := time.NewTicker(time.Hour)
		defer ticker.Stop()
		for {
			select {
			case <-ctx.Done():
				return
			case now := <-ticker.C:
				s.cleanupExpiredLocalTempAssets(ctx, now)
			}
		}
	}()
}

func (s *Server) cleanupExpiredLocalTempAssets(ctx context.Context, now time.Time) int {
	targets := []localTempAssetCleanupTarget{
		{
			StorageDir:       s.cfg.LocalGeneratedStorageDir,
			FallbackDir:      config.DefaultLocalGeneratedStorageDir,
			MarkRequestAsset: false,
		},
		{
			StorageDir:       s.cfg.LocalUploadedStorageDir,
			FallbackDir:      config.DefaultLocalUploadedStorageDir,
			MarkRequestAsset: true,
		},
	}
	deleted := 0
	for _, target := range targets {
		deleted += s.cleanupExpiredLocalTempAssetsInDir(ctx, now, target)
	}
	deleted += s.cleanupExpiredLocalBinaryResults(now)
	return deleted
}

func (s *Server) cleanupExpiredLocalBinaryResults(now time.Time) int {
	storageDir := strings.TrimSpace(s.cfg.LocalGeneratedStorageDir)
	if storageDir == "" {
		storageDir = config.DefaultLocalGeneratedStorageDir
	}
	root := filepath.Join(storageDir, "results")
	ttlHours := s.cfg.LocalResultTTLHours
	if ttlHours <= 0 {
		ttlHours = 24
	}
	expiredBefore := now.Add(-time.Duration(ttlHours) * time.Hour)
	deleted := 0
	var directories []string
	err := filepath.WalkDir(root, func(path string, entry os.DirEntry, walkErr error) error {
		if walkErr != nil {
			if !errors.Is(walkErr, os.ErrNotExist) && s.logger != nil {
				s.logger.Warn("walk local binary result failed", "path", path, "error", walkErr)
			}
			return nil
		}
		if entry.IsDir() {
			if path != root {
				directories = append(directories, path)
			}
			return nil
		}
		if entry.Type()&os.ModeSymlink != 0 {
			return nil
		}
		info, infoErr := entry.Info()
		if infoErr != nil || info.ModTime().After(expiredBefore) {
			return nil
		}
		if removeErr := os.Remove(path); removeErr != nil && !errors.Is(removeErr, os.ErrNotExist) {
			if s.logger != nil {
				s.logger.Warn("remove local binary result failed", "path", path, "error", removeErr)
			}
			return nil
		}
		deleted++
		return nil
	})
	if err != nil && !errors.Is(err, os.ErrNotExist) && s.logger != nil {
		s.logger.Warn("scan local binary result root failed", "dir", root, "error", err)
	}
	for index := len(directories) - 1; index >= 0; index-- {
		_ = os.Remove(directories[index])
	}
	return deleted
}

type localTempAssetCleanupTarget struct {
	StorageDir       string
	FallbackDir      string
	MarkRequestAsset bool
}

func (s *Server) cleanupExpiredLocalTempAssetsInDir(ctx context.Context, now time.Time, target localTempAssetCleanupTarget) int {
	storageDir := strings.TrimSpace(target.StorageDir)
	if storageDir == "" {
		storageDir = target.FallbackDir
	}
	entries, err := os.ReadDir(storageDir)
	if err != nil {
		if !errors.Is(err, os.ErrNotExist) {
			s.logger.Warn("read local temp asset dir failed", "dir", storageDir, "error", err)
		}
		return 0
	}
	ttl := time.Duration(s.localTempAssetTTLHours()) * time.Hour
	expiredBefore := now.Add(-ttl)
	deleted := 0
	for _, entry := range entries {
		if entry.IsDir() {
			continue
		}
		info, err := entry.Info()
		if err != nil {
			continue
		}
		if info.ModTime().After(expiredBefore) {
			continue
		}
		localPath := filepath.Join(storageDir, entry.Name())
		if err := os.Remove(localPath); err != nil && !errors.Is(err, os.ErrNotExist) {
			s.logger.Warn("remove local temp asset failed", "path", localPath, "error", err)
			continue
		}
		deleted++
		if target.MarkRequestAsset && strings.HasPrefix(entry.Name(), requestAssetFilePrefix) && s.store != nil {
			if err := s.store.MarkRequestAssetExpiredByLocalPath(ctx, localPath, now); err != nil && !store.IsUndefinedDatabaseObject(err) {
				s.logger.Warn("mark local temp asset expired failed", "path", localPath, "error", err)
			}
		}
	}
	return deleted
}
