package main

import (
	"database/sql"
	"encoding/json"
	"log"
	"path/filepath"
	"strings"

	"fn-registry/functions/infra"
)

func (s *Server) ingestAll() error {
	issues, err := infra.ScanIssuesDir(s.issuesDir)
	if err != nil {
		return err
	}
	for _, iss := range issues {
		if err := s.upsertIssueRow(iss); err != nil {
			log.Printf("upsert issue %s: %v", iss.ID, err)
		}
	}

	flows, err := infra.ScanFlowsDir(s.flowsDir)
	if err != nil {
		return err
	}
	for _, fl := range flows {
		if err := s.upsertFlowRow(fl); err != nil {
			log.Printf("upsert flow %s: %v", fl.ID, err)
		}
	}

	log.Printf("ingested %d issues, %d flows", len(issues), len(flows))
	return nil
}

func (s *Server) upsertIssueRow(iss infra.Issue) error {
	body, err := readBody(iss.FilePath)
	if err != nil {
		return err
	}
	_, err = s.db.Exec(`
		INSERT INTO issues (id,title,status,type,scope,priority,domain_json,tags_json,depends_json,blocks_json,related_json,flow_id,body,file_path,mtime_ns,created_at,updated_at,completed)
		VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
		ON CONFLICT(id) DO UPDATE SET
		  title=excluded.title, status=excluded.status, type=excluded.type, scope=excluded.scope,
		  priority=excluded.priority, domain_json=excluded.domain_json, tags_json=excluded.tags_json,
		  depends_json=excluded.depends_json, blocks_json=excluded.blocks_json, related_json=excluded.related_json,
		  flow_id=excluded.flow_id, body=excluded.body, file_path=excluded.file_path, mtime_ns=excluded.mtime_ns,
		  created_at=excluded.created_at, updated_at=excluded.updated_at, completed=excluded.completed
	`,
		iss.ID, iss.Title, iss.Status, iss.Type, iss.Scope, iss.Priority,
		jsonOrEmpty(iss.Domain), jsonOrEmpty(iss.Tags),
		jsonOrEmpty(iss.Depends), jsonOrEmpty(iss.Blocks), jsonOrEmpty(iss.Related),
		iss.Flow, string(body), iss.FilePath, iss.MtimeNs,
		iss.Created, iss.Updated, boolInt(iss.Completed),
	)
	return err
}

func (s *Server) upsertFlowRow(fl infra.Flow) error {
	body, err := readBody(fl.FilePath)
	if err != nil {
		return err
	}
	_, err = s.db.Exec(`
		INSERT INTO flows (id,title,status,kind,tags_json,body,file_path,mtime_ns)
		VALUES (?,?,?,?,?,?,?,?)
		ON CONFLICT(id) DO UPDATE SET
		  title=excluded.title, status=excluded.status, kind=excluded.kind,
		  tags_json=excluded.tags_json, body=excluded.body, file_path=excluded.file_path, mtime_ns=excluded.mtime_ns
	`,
		fl.ID, fl.Title, fl.Status, fl.Kind, jsonOrEmpty(fl.Tags),
		string(body), fl.FilePath, fl.MtimeNs,
	)
	return err
}

func readBody(path string) ([]byte, error) {
	_, body, err := infra.ParseIssueMd(path)
	if err != nil {
		return nil, err
	}
	return body, nil
}

func jsonOrEmpty(xs []string) string {
	if len(xs) == 0 {
		return "[]"
	}
	b, _ := json.Marshal(xs)
	return string(b)
}

func boolInt(b bool) int {
	if b {
		return 1
	}
	return 0
}

func (s *Server) deleteIssue(id string) error {
	_, err := s.db.Exec(`DELETE FROM issues WHERE id=?`, id)
	return err
}

func (s *Server) deleteFlow(id string) error {
	_, err := s.db.Exec(`DELETE FROM flows WHERE id=?`, id)
	return err
}

func issueIDFromPath(path, issuesDir string) string {
	rel, err := filepath.Rel(issuesDir, path)
	if err != nil {
		return ""
	}
	base := filepath.Base(rel)
	if !strings.HasSuffix(base, ".md") {
		return ""
	}
	name := strings.TrimSuffix(base, ".md")
	for i, r := range name {
		if r == '-' {
			return name[:i]
		}
		if r < '0' || r > '9' {
			if i == 0 {
				return ""
			}
			return name[:i]
		}
	}
	return name
}

var _ = sql.ErrNoRows
