// shared database related code
package models

import (
	"database/sql"
	"time"

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

// TODO consider splitting this stuff out into packages

type SignupNote struct {
	ID       int64
	Created  time.Time
	Author   string
	Content  string
	SignupID int64
}

func (n *SignupNote) Insert(db *sql.DB) error {
	n.Created = time.Now()
	stmt, err := db.Prepare(`
		INSERT INTO notes (created, author, content, signupid)
		VALUES (
			?, ?, ?, ?
		)`)
	if err != nil {
		return err
	}

	result, err := stmt.Exec(
		n.Created.Unix(),
		n.Author,
		n.Content,
		n.SignupID)
	if err != nil {
		return err
	}
	defer stmt.Close()

	liid, err := result.LastInsertId()
	if err != nil {
		return err
	}

	n.ID = liid

	return nil
}

type SignupDecision string

const (
	SignupAccepted SignupDecision = "accepted"
	SignupRejected SignupDecision = "rejected"
)

type TownSignup struct {
	ID      int64
	Created time.Time
	Email   string
	How     string
	Why     string
	Links   string

	Notes        []SignupNote
	Decision     SignupDecision
	DecisionTime time.Time
	DecidedBy    string
	CleanEmail   string
}

func (s *TownSignup) Insert(db *sql.DB) error {
	stmt, err := db.Prepare(`
		INSERT INTO signups (created, email, how, why, links) VALUES(
			?, ?, ?, ?, ?
		) RETURNING id
	`)
	if err != nil {
		return err
	}

	result, err := stmt.Exec(s.Created.Unix(), s.Email, s.How, s.Why, s.Links)
	if err != nil {
		return err
	}

	defer stmt.Close()

	liid, err := result.LastInsertId()
	if err != nil {
		return err
	}

	s.ID = liid

	return nil
}

func (s *TownSignup) RefreshNotes(db *sql.DB) error {
	stmt, err := db.Prepare(`
		SELECT created, author, content
		FROM notes
		WHERE signupid = ?
		ORDER BY created ASC`)
	if err != nil {
		return err
	}

	rows, err := stmt.Query(s.ID)
	if err != nil {
		return err
	}
	defer stmt.Close()

	s.Notes = []SignupNote{}

	for rows.Next() {
		note := &SignupNote{}
		var timestamp int64
		err = rows.Scan(
			&timestamp,
			&note.Author,
			&note.Content,
		)
		if err != nil {
			return err
		}
		note.Created = time.Unix(timestamp, 0)
		s.Notes = append(s.Notes, *note)
	}

	return nil
}

func (s *TownSignup) Review(db *sql.DB) error {
	stmt, err := db.Prepare(`
	UPDATE signups SET
		decision = ?,
		decision_time = ?,
		decided_by = ?,
		clean_email = ?
	WHERE id = ?`)
	if err != nil {
		return err
	}

	_, err = stmt.Exec(
		s.Decision,
		s.DecisionTime.Unix(),
		s.DecidedBy,
		s.CleanEmail,
		s.ID)
	if err != nil {
		return err
	}

	stmt.Close()

	return nil
}

func (s *TownSignup) All(db *sql.DB) ([]*TownSignup, error) {
	// TODO notes; circle back once can author them
	rows, err := db.Query(`
		SELECT 
			id, created, email, how, why, links
		FROM signups WHERE decision IS NULL`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	out := []*TownSignup{}
	for rows.Next() {
		su := &TownSignup{}
		var timestamp int64
		if err = rows.Scan(
			&su.ID,
			&timestamp,
			&su.Email,
			&su.How,
			&su.Why,
			&su.Links,
		); err != nil {
			return nil, err
		}

		su.Created = time.Unix(timestamp, 0)

		out = append(out, su)
	}

	return out, nil
}

type SuspiciousHost struct {
	ID         int64
	Domain     string
	CommonName string
	Tier       int64
}

func SuspiciousHosts(db *sql.DB) ([]SuspiciousHost, error) {
	rows, err := db.Query(`SELECT id, domain, common_name, tier FROM suspicious_hosts`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	out := []SuspiciousHost{}
	for rows.Next() {
		sh := SuspiciousHost{}
		if err = rows.Scan(
			&sh.ID,
			&sh.Domain,
			&sh.CommonName,
			&sh.Tier,
		); err != nil {
			return nil, err
		}

		out = append(out, sh)
	}

	return out, nil
}
