package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"

	"git.godopu.com/lab/landing_page/backend/internal/config"
	"git.godopu.com/lab/landing_page/backend/internal/db"
)

type SeedProject struct {
	Slug           string   `json:"slug"`
	Title          string   `json:"title"`
	Abstract       string   `json:"abstract"`
	Keywords       []string `json:"keywords"`
	StandardsTrack *string  `json:"standardsTrack"`
	StandardsOrg   *string  `json:"standardsOrg"`
	Period         *string  `json:"period"`
	Funder         *string  `json:"funder"`
}

type SeedArea struct {
	NameEn       string  `json:"name_en"`
	NameKr       *string `json:"name_kr"`
	DisplayOrder int     `json:"display_order"`
}

type SeedMember struct {
	NameKo       string  `json:"name_ko"`
	NameEn       string  `json:"name_en"`
	Degree       string  `json:"degree"`
	Affiliation  *string `json:"affiliation"`
	Email        *string `json:"email"`
	IsAdvisor    bool    `json:"is_advisor"`
	DisplayOrder int     `json:"display_order"`
}

type SeedAlumnus struct {
	NameKo          string  `json:"name_ko"`
	NameEn          string  `json:"name_en"`
	Degree          string  `json:"degree"`
	GraduatedAt     string  `json:"graduated_at"`
	Major           *string `json:"major"`
	CurrentPosition *string `json:"current_position"`
}

type SeedPublication struct {
	Category    string  `json:"category"`
	Title       string  `json:"title"`
	Authors     *string `json:"authors"`
	Venue       string  `json:"venue"`
	Volume      *string `json:"volume"`
	PublishedAt string  `json:"published_at"`
	KCI         bool    `json:"kci"`
	DOI         *string `json:"doi"`
	IsHighlight bool    `json:"is_highlight"`
}

type SeedPatent struct {
	Title          string  `json:"title"`
	Inventors      string  `json:"inventors"`
	ApplicationNo  string  `json:"application_no"`
	ApplicationAt  string  `json:"application_at"`
	RegistrationNo *string `json:"registration_no"`
	RegistrationAt *string `json:"registration_at"`
	Country        string  `json:"country"`
	PublishedAt    string  `json:"published_at"`
}

type SeedCourse struct {
	Code string  `json:"code"`
	En   string  `json:"en"`
	Note *string `json:"note"`
}

type SeedSemester struct {
	Term    string       `json:"term"`
	Year    int          `json:"year"`
	Courses []SeedCourse `json:"courses"`
}

type SeedDoc struct {
	Title       string  `json:"title"`
	Ref         string  `json:"ref"`
	Status      string  `json:"status"`
	PublishedAt *string `json:"publishedAt"`
}

type SeedStandardProject struct {
	WG        string    `json:"wg"`
	Name      string    `json:"name"`
	Documents []SeedDoc `json:"documents"`
}

type SeedStandardsBody struct {
	Org      string                `json:"org"`
	Full     string                `json:"full"`
	Scope    string                `json:"scope"`
	Period   string                `json:"period"`
	Role     *string               `json:"role"`
	Projects []SeedStandardProject `json:"projects"`
}

func readJSONFile[T any](path string) (T, error) {
	var result T
	data, err := os.ReadFile(path)
	if err != nil {
		return result, fmt.Errorf("read file %s: %w", path, err)
	}
	if err := json.Unmarshal(data, &result); err != nil {
		return result, fmt.Errorf("unmarshal %s: %w", path, err)
	}
	return result, nil
}

func main() {
	config.LoadEnvFile()
	defaultDB := "anl.db"
	if envDB := os.Getenv("DB_PATH"); envDB != "" {
		defaultDB = envDB
	}

	dbPath := flag.String("db", defaultDB, "SQLite database file path")
	seedDir := flag.String("seed-dir", "seed/data", "Directory containing seed JSON files")
	flag.Parse()

	log.Printf("Connecting to SQLite database at %s...", *dbPath)
	database, err := db.Connect(*dbPath)
	if err != nil {
		log.Fatalf("Database connection failed: %v", err)
	}
	defer database.Close()

	log.Println("Applying schema migrations...")
	if err := db.RunMigrations(database); err != nil {
		log.Fatalf("Migration failed: %v", err)
	}

	tx, err := database.Begin()
	if err != nil {
		log.Fatalf("Begin transaction failed: %v", err)
	}
	defer tx.Rollback()

	// Clear existing records in reverse FK order
	tables := []string{
		"standard_documents", "standards_bodies",
		"courses", "semesters",
		"patents", "publications",
		"alumni", "members",
		"research_areas", "research_projects",
	}
	for _, t := range tables {
		if _, err := tx.Exec("DELETE FROM " + t); err != nil {
			log.Fatalf("Failed to clear table %s: %v", t, err)
		}
	}

	// 1. Research Projects
	projects, err := readJSONFile[[]SeedProject](filepath.Join(*seedDir, "research_projects.json"))
	if err != nil {
		log.Fatalf("Failed to read research projects: %v", err)
	}
	for _, p := range projects {
		kwJSON, _ := json.Marshal(p.Keywords)
		_, err := tx.Exec(`
			INSERT INTO research_projects (slug, title, abstract, keywords, organization, standards_org, period, funder)
			VALUES (?, ?, ?, ?, ?, ?, ?, ?)
		`, p.Slug, p.Title, p.Abstract, string(kwJSON), p.StandardsTrack, p.StandardsOrg, p.Period, p.Funder)
		if err != nil {
			log.Fatalf("Insert research project (%s) failed: %v", p.Slug, err)
		}
	}

	// 2. Research Areas
	areas, err := readJSONFile[[]SeedArea](filepath.Join(*seedDir, "research_areas.json"))
	if err != nil {
		log.Fatalf("Failed to read research areas: %v", err)
	}
	for _, a := range areas {
		_, err := tx.Exec(`
			INSERT INTO research_areas (name_en, name_kr, display_order)
			VALUES (?, ?, ?)
		`, a.NameEn, a.NameKr, a.DisplayOrder)
		if err != nil {
			log.Fatalf("Insert research area failed: %v", err)
		}
	}

	// 3. Members
	members, err := readJSONFile[[]SeedMember](filepath.Join(*seedDir, "members.json"))
	if err != nil {
		log.Fatalf("Failed to read members: %v", err)
	}
	for _, m := range members {
		_, err := tx.Exec(`
			INSERT INTO members (name_ko, name_en, degree, affiliation, email, is_advisor, display_order)
			VALUES (?, ?, ?, ?, ?, ?, ?)
		`, m.NameKo, m.NameEn, m.Degree, m.Affiliation, m.Email, m.IsAdvisor, m.DisplayOrder)
		if err != nil {
			log.Fatalf("Insert member (%s) failed: %v", m.NameEn, err)
		}
	}

	// 4. Alumni
	alumni, err := readJSONFile[[]SeedAlumnus](filepath.Join(*seedDir, "alumni.json"))
	if err != nil {
		log.Fatalf("Failed to read alumni: %v", err)
	}
	for _, a := range alumni {
		_, err := tx.Exec(`
			INSERT INTO alumni (name_ko, name_en, degree, graduated_at, major, current_position)
			VALUES (?, ?, ?, ?, ?, ?)
		`, a.NameKo, a.NameEn, a.Degree, a.GraduatedAt, a.Major, a.CurrentPosition)
		if err != nil {
			log.Fatalf("Insert alumnus (%s) failed: %v", a.NameEn, err)
		}
	}

	// 5. Publications
	pubs, err := readJSONFile[[]SeedPublication](filepath.Join(*seedDir, "publications.json"))
	if err != nil {
		log.Fatalf("Failed to read publications: %v", err)
	}
	for _, p := range pubs {
		_, err := tx.Exec(`
			INSERT INTO publications (category, title, authors, venue, volume, published_at, kci, doi, is_highlight)
			VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
		`, p.Category, p.Title, p.Authors, p.Venue, p.Volume, p.PublishedAt, p.KCI, p.DOI, p.IsHighlight)
		if err != nil {
			log.Fatalf("Insert publication (%s) failed: %v", p.Title, err)
		}
	}

	// 6. Patents
	patents, err := readJSONFile[[]SeedPatent](filepath.Join(*seedDir, "patents.json"))
	if err != nil {
		log.Fatalf("Failed to read patents: %v", err)
	}
	for _, pat := range patents {
		_, err := tx.Exec(`
			INSERT INTO patents (title, inventors, application_no, application_at, registration_no, registration_at, country, published_at)
			VALUES (?, ?, ?, ?, ?, ?, ?, ?)
		`, pat.Title, pat.Inventors, pat.ApplicationNo, pat.ApplicationAt, pat.RegistrationNo, pat.RegistrationAt, pat.Country, pat.PublishedAt)
		if err != nil {
			log.Fatalf("Insert patent (%s) failed: %v", pat.Title, err)
		}
	}

	// 7. Semesters & Courses
	semesters, err := readJSONFile[[]SeedSemester](filepath.Join(*seedDir, "lectures.json"))
	if err != nil {
		log.Fatalf("Failed to read lectures: %v", err)
	}
	for _, s := range semesters {
		res, err := tx.Exec(`INSERT INTO semesters (year, term) VALUES (?, ?)`, s.Year, s.Term)
		if err != nil {
			log.Fatalf("Insert semester (%d %s) failed: %v", s.Year, s.Term, err)
		}
		semID, _ := res.LastInsertId()

		for cIdx, c := range s.Courses {
			_, err := tx.Exec(`
				INSERT INTO courses (semester_id, code, name_en, name_kr, note, display_order)
				VALUES (?, ?, ?, ?, ?, ?)
			`, semID, c.Code, c.En, nil, c.Note, cIdx+1)
			if err != nil {
				log.Fatalf("Insert course (%s) failed: %v", c.Code, err)
			}
		}
	}

	// 8. Standards Bodies & Documents
	standards, err := readJSONFile[[]SeedStandardsBody](filepath.Join(*seedDir, "standards.json"))
	if err != nil {
		log.Fatalf("Failed to read standards: %v", err)
	}
	for bIdx, b := range standards {
		res, err := tx.Exec(`
			INSERT INTO standards_bodies (org, full_name, scope, period, role, display_order)
			VALUES (?, ?, ?, ?, ?, ?)
		`, b.Org, b.Full, b.Scope, b.Period, b.Role, bIdx+1)
		if err != nil {
			log.Fatalf("Insert standards body (%s) failed: %v", b.Org, err)
		}
		bodyID, _ := res.LastInsertId()

		for _, p := range b.Projects {
			for _, d := range p.Documents {
				_, err := tx.Exec(`
					INSERT INTO standard_documents (body_id, wg, project_name, title, doc_ref, status, published_at)
					VALUES (?, ?, ?, ?, ?, ?, ?)
				`, bodyID, p.WG, p.Name, d.Title, d.Ref, d.Status, d.PublishedAt)
				if err != nil {
					log.Fatalf("Insert standard document (%s) failed: %v", d.Title, err)
				}
			}
		}
	}

	if err := tx.Commit(); err != nil {
		log.Fatalf("Transaction commit failed: %v", err)
	}
	log.Println("Transaction committed successfully.")

	// Verify Counts
	log.Println("=== Verifying Seeding Counts ===")
	checkCount := func(table string, expected int) {
		var cnt int
		if err := database.QueryRow("SELECT COUNT(*) FROM " + table).Scan(&cnt); err != nil {
			log.Fatalf("Count check failed for %s: %v", table, err)
		}
		if cnt != expected {
			log.Fatalf("Count mismatch for %s: expected %d, got %d", table, expected, cnt)
		}
		log.Printf("✓ %-20s : %3d rows (matches expected)", table, cnt)
	}

	checkCount("research_projects", 3)
	checkCount("research_areas", 14)
	checkCount("members", 16)
	checkCount("alumni", 60)
	checkCount("publications", 148)
	checkCount("patents", 75)
	checkCount("semesters", 45)
	checkCount("courses", 132)
	checkCount("standards_bodies", 6)
	checkCount("standard_documents", 44)

	// Explicit highlight assertion
	var hlCount int
	if err := database.QueryRow("SELECT COUNT(*) FROM publications WHERE is_highlight = 1").Scan(&hlCount); err != nil {
		log.Fatalf("Highlight count query failed: %v", err)
	}
	if hlCount != 5 {
		log.Fatalf("Highlight count mismatch: expected exactly 5, got %d", hlCount)
	}
	log.Printf("✓ %-20s : %3d rows (exactly 5 highlights)", "publications(highlight)", hlCount)

	log.Println("=== Seeding & Verification Completed Successfully! ===")
}
