package daemon

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"os"
	"time"

	"huntr/internal/pidfile"
)

// StatusInfo holds all information needed to display scan status.
// Populated by QueryStatus from the database and PID file.
type StatusInfo struct {
	// Process status
	Running bool `json:"running"`
	PID     int  `json:"pid,omitempty"`

	// Session info (from most recent session)
	HasSession       bool       `json:"has_session"`
	SessionName      string     `json:"session_name,omitempty"`
	SessionStatus    string     `json:"session_status,omitempty"`
	TotalTargets     int        `json:"total_targets,omitempty"`
	CompletedTargets int        `json:"completed_targets,omitempty"`
	StartedAt        *time.Time `json:"started_at,omitempty"`
	UpdatedAt        *time.Time `json:"updated_at,omitempty"`
	CompletedAt      *time.Time `json:"completed_at,omitempty"`

	// Aggregate stats
	TotalFindings int64 `json:"total_findings"`
	TotalDomains  int64 `json:"total_domains"`
	TotalSessions int64 `json:"total_sessions"`
}

// QueryStatus gathers status information from the database and PID file.
// Works whether a scan is running or not -- does NOT create a PID file
// or block on PID check.
//
// The function queries:
//   - PID file for process liveness
//   - Most recent session (active first, then any) for progress
//   - Aggregate finding/domain counts
func QueryStatus(db *sql.DB, pidPath string) (*StatusInfo, error) {
	info := &StatusInfo{}

	// -- Check PID file for running instance --
	pid, err := pidfile.ReadPID(pidPath)
	if err == nil {
		info.PID = pid
		info.Running = pidfile.IsProcessRunning(pid)
	}
	// If PID file doesn't exist or is invalid, Running stays false

	// -- Query most recent session (prefer active, then most recent by updated_at) --
	var sessionID int64
	var sessionName, sessionStatus string
	var totalTargets, completedTargets int
	var startedAt, updatedAt time.Time
	var completedAt sql.NullTime

	// Try active session first
	err = db.QueryRow(`
		SELECT id, name, status, total_targets, completed_targets,
		       started_at, updated_at, completed_at
		FROM sessions
		WHERE status = 'active'
		ORDER BY updated_at DESC
		LIMIT 1
	`).Scan(&sessionID, &sessionName, &sessionStatus, &totalTargets,
		&completedTargets, &startedAt, &updatedAt, &completedAt)

	if err == sql.ErrNoRows {
		// No active session -- get the most recent session of any status
		err = db.QueryRow(`
			SELECT id, name, status, total_targets, completed_targets,
			       started_at, updated_at, completed_at
			FROM sessions
			ORDER BY updated_at DESC
			LIMIT 1
		`).Scan(&sessionID, &sessionName, &sessionStatus, &totalTargets,
			&completedTargets, &startedAt, &updatedAt, &completedAt)
	}

	if err == nil {
		info.HasSession = true
		info.SessionName = sessionName
		info.SessionStatus = sessionStatus
		info.TotalTargets = totalTargets
		info.CompletedTargets = completedTargets
		info.StartedAt = &startedAt
		info.UpdatedAt = &updatedAt
		if completedAt.Valid {
			t := completedAt.Time
			info.CompletedAt = &t
		}
	} else if err != sql.ErrNoRows {
		return nil, fmt.Errorf("query session: %w", err)
	}

	// -- Aggregate stats --
	db.QueryRow(`SELECT COUNT(*) FROM findings`).Scan(&info.TotalFindings)
	db.QueryRow(`SELECT COUNT(DISTINCT domain) FROM findings`).Scan(&info.TotalDomains)
	db.QueryRow(`SELECT COUNT(*) FROM sessions`).Scan(&info.TotalSessions)

	return info, nil
}

// PrintHumanStatus prints StatusInfo in a human-readable format to stdout.
func PrintHumanStatus(info *StatusInfo) {
	fmt.Println("huntr status")
	fmt.Println("============")
	fmt.Println()

	// Process status
	if info.Running {
		fmt.Printf("  Instance:  RUNNING (PID %d)\n", info.PID)
	} else {
		fmt.Println("  Instance:  not running")
	}
	fmt.Println()

	// Session info
	if info.HasSession {
		fmt.Printf("  Session:   %s\n", info.SessionName)
		fmt.Printf("  Status:    %s\n", info.SessionStatus)
		if info.TotalTargets > 0 {
			pct := float64(info.CompletedTargets) / float64(info.TotalTargets) * 100
			fmt.Printf("  Progress:  %d/%d targets (%.1f%%)\n",
				info.CompletedTargets, info.TotalTargets, pct)
		}
		if info.StartedAt != nil {
			fmt.Printf("  Started:   %s\n", info.StartedAt.Format("2006-01-02 15:04:05"))
		}
		if info.CompletedAt != nil {
			fmt.Printf("  Completed: %s\n", info.CompletedAt.Format("2006-01-02 15:04:05"))
		} else if info.UpdatedAt != nil {
			fmt.Printf("  Updated:   %s\n", info.UpdatedAt.Format("2006-01-02 15:04:05"))
		}
	} else {
		fmt.Println("  Session:   no sessions found")
	}
	fmt.Println()

	// Aggregate stats
	fmt.Println("  Database:")
	fmt.Printf("    Findings: %d\n", info.TotalFindings)
	fmt.Printf("    Domains:  %d\n", info.TotalDomains)
	fmt.Printf("    Sessions: %d\n", info.TotalSessions)
}

// PrintJSONStatus prints StatusInfo as machine-readable JSON to stdout.
func PrintJSONStatus(info *StatusInfo) error {
	enc := json.NewEncoder(os.Stdout)
	enc.SetIndent("", "  ")
	return enc.Encode(info)
}
