package database

import (
	"database/sql"
	"time"
)

// APIKey represents an API key record
type APIKey struct {
	ID          int64
	Key         string
	Description string
	CreatedAt   time.Time
	UpdatedAt   time.Time
}

// ProjectMapping represents a project to Jenkins job mapping
type ProjectMapping struct {
	ID             int64
	RepositoryName string
	DefaultJob     string
	BranchJobs     []BranchJob
	BranchPatterns []BranchPattern
	CreatedAt      time.Time
	UpdatedAt      time.Time
}

// BranchJob represents a branch to job mapping
type BranchJob struct {
	ID         int64
	ProjectID  int64
	BranchName string
	JobName    string
	CreatedAt  time.Time
	UpdatedAt  time.Time
}

// BranchPattern represents a branch pattern to job mapping
type BranchPattern struct {
	ID        int64
	ProjectID int64
	Pattern   string
	JobName   string
	CreatedAt time.Time
	UpdatedAt time.Time
}

// TriggerLog represents a job trigger log entry
type TriggerLog struct {
	ID             int64
	RepositoryName string
	BranchName     string
	CommitSHA      string
	JobName        string
	Status         string
	ErrorMessage   string
	CreatedAt      time.Time
}

// CreateAPIKey creates a new API key
func (db *DB) CreateAPIKey(key *APIKey) error {
	query := `
		INSERT INTO api_keys (key, description)
		VALUES (?, ?)`

	result, err := db.Exec(query, key.Key, key.Description)
	if err != nil {
		return err
	}

	key.ID, _ = result.LastInsertId()
	return nil
}

// GetAPIKey retrieves an API key by its value
func (db *DB) GetAPIKey(key string) (*APIKey, error) {
	var apiKey APIKey
	query := `
		SELECT id, key, description, created_at, updated_at
		FROM api_keys
		WHERE key = ?`

	err := db.QueryRow(query, key).Scan(
		&apiKey.ID,
		&apiKey.Key,
		&apiKey.Description,
		&apiKey.CreatedAt,
		&apiKey.UpdatedAt,
	)

	if err == sql.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}

	return &apiKey, nil
}

// DeleteAPIKey deletes an API key by its value
func (db *DB) DeleteAPIKey(key string) error {
	query := `DELETE FROM api_keys WHERE key = ?`
	result, err := db.Exec(query, key)
	if err != nil {
		return err
	}

	affected, err := result.RowsAffected()
	if err != nil {
		return err
	}

	if affected == 0 {
		return sql.ErrNoRows
	}

	return nil
}

// GetAPIKeys retrieves all API keys
func (db *DB) GetAPIKeys() ([]APIKey, error) {
	query := `
		SELECT id, key, description, created_at, updated_at
		FROM api_keys
		ORDER BY created_at DESC`

	rows, err := db.Query(query)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var keys []APIKey
	for rows.Next() {
		var key APIKey
		err := rows.Scan(
			&key.ID,
			&key.Key,
			&key.Description,
			&key.CreatedAt,
			&key.UpdatedAt,
		)
		if err != nil {
			return nil, err
		}
		keys = append(keys, key)
	}

	return keys, nil
}

// CreateProjectMapping creates a new project mapping
func (db *DB) CreateProjectMapping(project *ProjectMapping) error {
	tx, err := db.Begin()
	if err != nil {
		return err
	}
	defer tx.Rollback()

	// Insert project mapping
	query := `
		INSERT INTO project_mappings (repository_name, default_job)
		VALUES (?, ?)`

	result, err := tx.Exec(query, project.RepositoryName, project.DefaultJob)
	if err != nil {
		return err
	}

	projectID, _ := result.LastInsertId()
	project.ID = projectID

	// Insert branch jobs
	for _, job := range project.BranchJobs {
		query = `
			INSERT INTO branch_jobs (project_id, branch_name, job_name)
			VALUES (?, ?, ?)`

		_, err = tx.Exec(query, projectID, job.BranchName, job.JobName)
		if err != nil {
			return err
		}
	}

	// Insert branch patterns
	for _, pattern := range project.BranchPatterns {
		query = `
			INSERT INTO branch_patterns (project_id, pattern, job_name)
			VALUES (?, ?, ?)`

		_, err = tx.Exec(query, projectID, pattern.Pattern, pattern.JobName)
		if err != nil {
			return err
		}
	}

	return tx.Commit()
}

// GetProjectMapping retrieves a project mapping by repository name
func (db *DB) GetProjectMapping(repoName string) (*ProjectMapping, error) {
	var project ProjectMapping

	// Get project mapping
	query := `
		SELECT id, repository_name, default_job, created_at, updated_at
		FROM project_mappings
		WHERE repository_name = ?`

	err := db.QueryRow(query, repoName).Scan(
		&project.ID,
		&project.RepositoryName,
		&project.DefaultJob,
		&project.CreatedAt,
		&project.UpdatedAt,
	)

	if err == sql.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}

	// Get branch jobs
	query = `
		SELECT id, branch_name, job_name, created_at, updated_at
		FROM branch_jobs
		WHERE project_id = ?`

	rows, err := db.Query(query, project.ID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		var job BranchJob
		err := rows.Scan(
			&job.ID,
			&job.BranchName,
			&job.JobName,
			&job.CreatedAt,
			&job.UpdatedAt,
		)
		if err != nil {
			return nil, err
		}
		project.BranchJobs = append(project.BranchJobs, job)
	}

	// Get branch patterns
	query = `
		SELECT id, pattern, job_name, created_at, updated_at
		FROM branch_patterns
		WHERE project_id = ?`

	rows, err = db.Query(query, project.ID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		var pattern BranchPattern
		err := rows.Scan(
			&pattern.ID,
			&pattern.Pattern,
			&pattern.JobName,
			&pattern.CreatedAt,
			&pattern.UpdatedAt,
		)
		if err != nil {
			return nil, err
		}
		project.BranchPatterns = append(project.BranchPatterns, pattern)
	}

	return &project, nil
}

// LogTrigger logs a job trigger event
func (db *DB) LogTrigger(log *TriggerLog) error {
	query := `
		INSERT INTO trigger_logs (repository_name, branch_name, commit_sha, job_name, status, error_message)
		VALUES (?, ?, ?, ?, ?, ?)`

	result, err := db.Exec(query,
		log.RepositoryName,
		log.BranchName,
		log.CommitSHA,
		log.JobName,
		log.Status,
		log.ErrorMessage,
	)
	if err != nil {
		return err
	}

	log.ID, _ = result.LastInsertId()
	return nil
}

// GetTriggerLogs retrieves trigger logs with optional filters
func (db *DB) GetTriggerLogs(repoName, branchName string, since time.Time, limit int) ([]TriggerLog, error) {
	query := `
		SELECT id, repository_name, branch_name, commit_sha, job_name, status, error_message, created_at
		FROM trigger_logs
		WHERE 1=1`
	args := []interface{}{}

	if repoName != "" {
		query += " AND repository_name = ?"
		args = append(args, repoName)
	}
	if branchName != "" {
		query += " AND branch_name = ?"
		args = append(args, branchName)
	}
	if !since.IsZero() {
		query += " AND created_at >= ?"
		args = append(args, since)
	}

	query += " ORDER BY created_at DESC"
	if limit > 0 {
		query += " LIMIT ?"
		args = append(args, limit)
	}

	rows, err := db.Query(query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var logs []TriggerLog
	for rows.Next() {
		var log TriggerLog
		err := rows.Scan(
			&log.ID,
			&log.RepositoryName,
			&log.BranchName,
			&log.CommitSHA,
			&log.JobName,
			&log.Status,
			&log.ErrorMessage,
			&log.CreatedAt,
		)
		if err != nil {
			return nil, err
		}
		logs = append(logs, log)
	}

	return logs, nil
}
