package main

// Dosya indirme (Ac / Indir) destegi (SDK surumu).
// "ara" sonucundaki dosyalar kisa sureli izin listesine yazilir (%APPDATA%\FikoYT\everything-indirme-izin.json).
// "dosya-parca" yalnizca bu listedeki, sure ve boyut sinirina uyan dosyalarin parcasini base64 olarak verir.

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"time"
)

const (
	indirmeParcaMaks    = 256 * 1024
	indirmeVarsayilanDk = 30
	indirmeVarsayilanMb = 2048
)

// izinHaritasi: tam yol -> izin bitis zamani (unix saniye).
type izinHaritasi map[string]int64

func izinDosyasi() (string, error) {
	taban := os.Getenv("APPDATA")
	if taban == "" {
		return "", errors.New("APPDATA bulunamadi")
	}
	dir := filepath.Join(taban, "FikoYT")
	if err := os.MkdirAll(dir, 0o700); err != nil {
		return "", err
	}
	return filepath.Join(dir, "everything-indirme-izin.json"), nil
}

func izinleriOku() izinHaritasi {
	h := izinHaritasi{}
	yol, err := izinDosyasi()
	if err != nil {
		return h
	}
	b, err := os.ReadFile(yol)
	if err != nil {
		return h
	}
	_ = json.Unmarshal(b, &h)
	return h
}

// izinleriYaz: suresi dolan kayitlari temizler ve dosyaya yazar.
func izinleriYaz(h izinHaritasi) {
	simdi := time.Now().Unix()
	for k, v := range h {
		if simdi > v {
			delete(h, k)
		}
	}
	yol, err := izinDosyasi()
	if err != nil {
		return
	}
	b, err := json.Marshal(h)
	if err != nil {
		return
	}
	_ = os.WriteFile(yol, b, 0o600)
}

// izinKaydet: arama sonucundaki dosyalari sureli izin listesine ekler.
func izinKaydet(veri json.RawMessage, sureDk int) {
	var s struct {
		Results []map[string]any `json:"results"`
	}
	if json.Unmarshal(veri, &s) != nil {
		return
	}
	if sureDk < 1 || sureDk > 1440 {
		sureDk = indirmeVarsayilanDk
	}
	bitis := time.Now().Add(time.Duration(sureDk) * time.Minute).Unix()
	h := izinleriOku()
	for _, r := range s.Results {
		if r["type"] != "file" {
			continue
		}
		ad, _ := r["name"].(string)
		yol, _ := r["path"].(string)
		if ad == "" || yol == "" {
			continue
		}
		h[yol+`\`+ad] = bitis
	}
	izinleriYaz(h)
}

func izinliMi(yol string) bool {
	bitis, ok := izinleriOku()[yol]
	return ok && time.Now().Unix() <= bitis
}

// parcaVeri: dosya-parca cevabi.
type parcaVeri struct {
	Toplam int64  `json:"toplam"`
	Veri   string `json:"veri,omitempty"`
}

// dosyaParca: izinli bir dosyadan [baslangic, baslangic+uzunluk) araligini okur.
func dosyaParca(yol string, baslangic, uzunluk int64, maksMb int) (parcaVeri, error) {
	if !izinliMi(yol) {
		return parcaVeri{}, errors.New("dosya izinli degil veya suresi doldu")
	}
	f, err := os.Open(yol)
	if err != nil {
		return parcaVeri{}, errors.New("dosya acilamadi")
	}
	defer f.Close()
	st, err := f.Stat()
	if err != nil || st.IsDir() {
		return parcaVeri{}, errors.New("dosya bulunamadi")
	}
	boyut := st.Size()
	if maksMb < 1 || maksMb > 4096 {
		maksMb = indirmeVarsayilanMb
	}
	if boyut > int64(maksMb)<<20 {
		return parcaVeri{}, fmt.Errorf("dosya en buyuk boyut sinirini (%d MB) asiyor", maksMb)
	}
	if baslangic < 0 || baslangic >= boyut {
		return parcaVeri{Toplam: boyut}, nil
	}
	if uzunluk <= 0 || uzunluk > indirmeParcaMaks {
		uzunluk = indirmeParcaMaks
	}
	if kalan := boyut - baslangic; uzunluk > kalan {
		uzunluk = kalan
	}
	buf := make([]byte, uzunluk)
	if _, err := f.Seek(baslangic, io.SeekStart); err != nil {
		return parcaVeri{}, errors.New("dosya okunamadi")
	}
	n, err := io.ReadFull(f, buf)
	if err != nil && err != io.ErrUnexpectedEOF {
		return parcaVeri{}, errors.New("dosya okunamadi")
	}
	return parcaVeri{Toplam: boyut, Veri: base64.StdEncoding.EncodeToString(buf[:n])}, nil
}
