// Path: Code/Tracker/db/sqlite.go

package db

import (
	"database/sql"
	"encoding/json"
	"os"
	"path/filepath"
	"time"

	_ "github.com/mattn/go-sqlite3"
)

type DB struct {
	conn *sql.DB
}

type Document struct {
	ID            int64
	FilePath      string
	FileHash      string
	FileSize      int64
	FirstSeen     time.Time
	LastModified  time.Time
	LastProcessed sql.NullTime
	Status        string // pending, processing, completed, failed
}

type Extraction struct {
	ID             int64
	DocumentID     int64
	ExtractionJSON string
	ExtractedAt    time.Time
}

type ProcessingLog struct {
	ID         int64
	DocumentID int64
	Action     string // processed, updated, skipped, failed
	Details    string
	CreatedAt  time.Time
}

func Open(dbPath string) (*DB, error) {
	// Ensure directory exists
	dir := filepath.Dir(dbPath)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return nil, err
	}

	conn, err := sql.Open("sqlite3", dbPath+"?_journal_mode=WAL")
	if err != nil {
		return nil, err
	}

	db := &DB{conn: conn}
	if err := db.migrate(); err != nil {
		conn.Close()
		return nil, err
	}

	return db, nil
}

func (db *DB) Close() error {
	return db.conn.Close()
}

func (db *DB) migrate() error {
	schema := `
	CREATE TABLE IF NOT EXISTS documents (
		id INTEGER PRIMARY KEY AUTOINCREMENT,
		file_path TEXT UNIQUE NOT NULL,
		file_hash TEXT NOT NULL,
		file_size INTEGER NOT NULL,
		first_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
		last_modified TIMESTAMP NOT NULL,
		last_processed TIMESTAMP,
		status TEXT DEFAULT 'pending'
	);

	CREATE INDEX IF NOT EXISTS idx_documents_path ON documents(file_path);
	CREATE INDEX IF NOT EXISTS idx_documents_status ON documents(status);

	CREATE TABLE IF NOT EXISTS extractions (
		id INTEGER PRIMARY KEY AUTOINCREMENT,
		document_id INTEGER NOT NULL REFERENCES documents(id),
		extraction_json TEXT NOT NULL,
		extracted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
	);

	CREATE INDEX IF NOT EXISTS idx_extractions_doc ON extractions(document_id);

	CREATE TABLE IF NOT EXISTS processing_log (
		id INTEGER PRIMARY KEY AUTOINCREMENT,
		document_id INTEGER NOT NULL REFERENCES documents(id),
		action TEXT NOT NULL,
		details TEXT,
		created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
	);

	CREATE INDEX IF NOT EXISTS idx_log_doc ON processing_log(document_id);
	`

	_, err := db.conn.Exec(schema)
	return err
}

// GetDocument retrieves a document by file path
func (db *DB) GetDocument(filePath string) (*Document, error) {
	row := db.conn.QueryRow(`
		SELECT id, file_path, file_hash, file_size, first_seen, last_modified, last_processed, status
		FROM documents WHERE file_path = ?
	`, filePath)

	var doc Document
	err := row.Scan(&doc.ID, &doc.FilePath, &doc.FileHash, &doc.FileSize,
		&doc.FirstSeen, &doc.LastModified, &doc.LastProcessed, &doc.Status)
	if err == sql.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	return &doc, nil
}

// UpsertDocument creates or updates a document record
func (db *DB) UpsertDocument(filePath, fileHash string, fileSize int64, lastModified time.Time) (*Document, error) {
	existing, err := db.GetDocument(filePath)
	if err != nil {
		return nil, err
	}

	if existing == nil {
		// Insert new
		result, err := db.conn.Exec(`
			INSERT INTO documents (file_path, file_hash, file_size, last_modified, status)
			VALUES (?, ?, ?, ?, 'pending')
		`, filePath, fileHash, fileSize, lastModified)
		if err != nil {
			return nil, err
		}
		id, _ := result.LastInsertId()
		return db.getDocumentByID(id)
	}

	// Update existing
	_, err = db.conn.Exec(`
		UPDATE documents SET file_hash = ?, file_size = ?, last_modified = ?, status = 'pending'
		WHERE id = ?
	`, fileHash, fileSize, lastModified, existing.ID)
	if err != nil {
		return nil, err
	}

	return db.getDocumentByID(existing.ID)
}

func (db *DB) getDocumentByID(id int64) (*Document, error) {
	row := db.conn.QueryRow(`
		SELECT id, file_path, file_hash, file_size, first_seen, last_modified, last_processed, status
		FROM documents WHERE id = ?
	`, id)

	var doc Document
	err := row.Scan(&doc.ID, &doc.FilePath, &doc.FileHash, &doc.FileSize,
		&doc.FirstSeen, &doc.LastModified, &doc.LastProcessed, &doc.Status)
	if err != nil {
		return nil, err
	}
	return &doc, nil
}

// UpdateStatus updates the status of a document
func (db *DB) UpdateStatus(docID int64, status string) error {
	_, err := db.conn.Exec(`UPDATE documents SET status = ? WHERE id = ?`, status, docID)
	return err
}

// MarkProcessed marks a document as successfully processed
func (db *DB) MarkProcessed(docID int64) error {
	_, err := db.conn.Exec(`
		UPDATE documents SET status = 'completed', last_processed = CURRENT_TIMESTAMP
		WHERE id = ?
	`, docID)
	return err
}

// SaveExtraction saves the extraction result
func (db *DB) SaveExtraction(docID int64, extractionJSON map[string]interface{}) error {
	jsonBytes, err := json.Marshal(extractionJSON)
	if err != nil {
		return err
	}

	_, err = db.conn.Exec(`
		INSERT INTO extractions (document_id, extraction_json) VALUES (?, ?)
	`, docID, string(jsonBytes))
	return err
}

// LogAction logs a processing action
func (db *DB) LogAction(docID int64, action, details string) error {
	_, err := db.conn.Exec(`
		INSERT INTO processing_log (document_id, action, details) VALUES (?, ?, ?)
	`, docID, action, details)
	return err
}

// NeedsProcessing checks if a document needs processing based on hash
func (db *DB) NeedsProcessing(filePath, currentHash string) (bool, error) {
	doc, err := db.GetDocument(filePath)
	if err != nil {
		return false, err
	}
	if doc == nil {
		return true, nil // New file
	}
	if doc.FileHash != currentHash {
		return true, nil // File changed
	}
	if doc.Status == "failed" {
		return true, nil // Retry failed
	}
	return false, nil // Already processed with same hash
}
