package postgres

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"log"
	"os"

	"github.com/lib/pq"

	"../news"
	"../utils"
)

type Postgres struct {
	host     	string
	port     	int
	username    string
	password 	string
	database   	string

	psqlInfo    string
	psqlConn    *sql.DB
	isConnected bool
}

var pg *Postgres

func init() {
	var err error

	pg = &Postgres{
		host:        os.Getenv("POSTGRES_HOST"),
		port:        utils.AtoI(os.Getenv("POSTGRES_PORT")),
		username:    os.Getenv("POSTGRES_USERNAME"),
		password:    os.Getenv("POSTGRES_PASSWORD"),
		database:    os.Getenv("POSTGRES_DATABASE"),
	}

	pg.psqlInfo = fmt.Sprintf(
		"host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
		pg.host, pg.port, pg.username, pg.password, pg.database,
	)

	pg.psqlConn, err = sql.Open("postgres", pg.psqlInfo)
	if err != nil {
		log.Fatalf("error while opening pg connection : %s", err)
	}

	if err = pg.psqlConn.Ping(); err != nil {
		log.Fatalf("error while pinging pg server : %s", err)
	}
	pg.isConnected = true
}

func Close() {
	if !pg.isConnected {
		return
	}
	if err := pg.psqlConn.Close(); err != nil {
		log.Fatalf("error while closing pg connection : %s", err)
	}
	pg.isConnected = false
}

func ListSports() ([]*news.Sport, error) {
	var sports []*news.Sport

	rows, err := pg.psqlConn.Query("SELECT id, name, clean_name FROM public.mainapp_sport")
	if err != nil {
		return nil, fmt.Errorf("error while querying postgres : %s", err)
	}

	for rows.Next() {
		sport := &news.Sport{}
		err = rows.Scan(&sport.Id, &sport.Name, &sport.CleanName)
		if err != nil {
			return nil, fmt.Errorf("error while scanning row from postgres : %s", err)
		}
		sports = append(sports, sport)
	}
	return sports, nil
}

func ListSources() ([]*news.Source, error) {
	var sources []*news.Source

	rows, err := pg.psqlConn.Query("SELECT id, name, clean_name, urls FROM public.mainapp_source")
	if err != nil {
		return nil, fmt.Errorf("error while querying postgres : %s", err)
	}

	for rows.Next() {
		source := &news.Source{}
		sourceUrls := ""
		err = rows.Scan(&source.Id, &source.Name, &source.CleanName, &sourceUrls)
		if err != nil {
			return nil, fmt.Errorf("error while scanning row from postgres : %s", err)
		}
		if err = json.Unmarshal([]byte(sourceUrls), &source.Urls); err != nil {
			return nil, fmt.Errorf("error while scanning row from postgres : %s", err)
		}
		sources = append(sources, source)
	}
	return sources, nil
}

func InsertNews(n *news.News) error {
	err := pg.psqlConn.QueryRow(`
			INSERT INTO public.mainapp_news 
				(title, clean_title, link, pub_date, description, image, teaser, author, 
				content, redirect, haystack, tags, clean_tags, error, trace, 
				league_id, source_id, sport_id, team_id)
			VALUES 
				($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)
			RETURNING 
				id
		`, n.Title, n.CleanTitle, n.Link, n.PubDate, utils.NullableString(n.Description),
		utils.NullableString(n.Image), utils.NullableString(n.Teaser), utils.NullableString(n.Author),
		pq.Array(n.Content), utils.NullableString(n.Redirect), utils.NullableString(n.Haystack),
		pq.Array(n.Tags), pq.Array(n.CleanTags), utils.NullableString(n.Error), utils.NullableString(n.Trace),
		utils.NullableInt(n.LeagueId), n.Source.Id, n.Sport.Id, utils.NullableInt(n.TeamId),
	).Scan(&n.Id)
	if err != nil {
		return err
	}
	return nil
}

func UpdateNews(n *news.News) error {
	if _, err := pg.psqlConn.Exec(`
			UPDATE public.mainapp_news
			SET title = $1, clean_title = $2, pub_date = $3, link = $4, description = $5, 
				image = $6, teaser = $7, author = $8, content = $9, redirect = $10, 
				haystack = $11, tags = $12, clean_tags = $13, error = $14, trace = $15,
				league_id = $16, source_id = $17, sport_id = $18, team_id = $19
			WHERE id = $20
		`, n.Title, n.CleanTitle, n.PubDate, n.Link, utils.NullableString(n.Description),
		utils.NullableString(n.Image), utils.NullableString(n.Teaser), utils.NullableString(n.Author),
		pq.Array(n.Content), utils.NullableString(n.Redirect), utils.NullableString(n.Haystack),
		pq.Array(n.Tags), pq.Array(n.CleanTags), utils.NullableString(n.Error), utils.NullableString(n.Trace),
		utils.NullableInt(n.LeagueId), n.Source.Id, n.Sport.Id, utils.NullableInt(n.TeamId), n.Id,
	); err != nil {
		return err
	}
	return nil
}
