package plg_backend_psql

import (
	"context"
	"database/sql"
	"slices"
	"strings"
	"time"

	. "github.com/mickael-kerjean/filestash/server/common"
)

func getPath(path string) (LocationRow, error) {
	l := LocationRow{}
	for i, chunk := range strings.Split(path, "/") {
		if i == 0 {
			if chunk != "" {
				return l, ErrNotValid
			}
		} else if i == 1 {
			if strings.Contains(chunk, `"`) {
				return l, ErrNotValid
			}
			l.table = chunk
		} else if i == 2 {
			l.row = strings.TrimSuffix(chunk, ".form")
		} else {
			return l, ErrNotValid
		}
	}
	return l, nil
}

func processTable(ctx context.Context, db *sql.DB, table string) ([]Column, string, error) {
	columns, err := _getColumns(ctx, db, table)
	if err != nil {
		return nil, "", err
	}
	key := ""
	score := 0
	for _, column := range columns {
		if c := _calculateScore(column); c > score {
			key = column.Name
			score = c
		}
	}
	if key == "" {
		return columns, "", ErrNotValid
	}
	return columns, key, nil
}

func _getColumns(ctx context.Context, db *sql.DB, table string) ([]Column, error) {
	rows, err := db.QueryContext(ctx, `
        SELECT
            c.column_name,
            c.udt_name as type,
            (c.is_nullable = 'YES') AS nullable,
            (c.column_default IS NOT NULL) AS has_default,
            coalesce(string_agg(tc.constraint_type, ', '), '') as constraint
        FROM information_schema.columns AS c
        LEFT JOIN information_schema.key_column_usage kcu USING (table_name, column_name)
        LEFT JOIN information_schema.table_constraints tc USING (table_name, constraint_name)
        WHERE c.table_name = $1
        GROUP BY c.column_name, c.is_nullable, c.udt_name, c.column_default
        ORDER BY MIN(c.ordinal_position)
    `, table)
	if err != nil {
		return nil, err
	}
	columns := []Column{}
	for rows.Next() {
		var c Column
		var constraints string
		if err := rows.Scan(&c.Name, &c.Type, &c.Nullable, &c.Default, &constraints); err != nil {
			return nil, err
		}
		c.Constraint = strings.Split(constraints, ", ")
		c.Table = table
		columns = append(columns, c)
	}
	return columns, rows.Close()
}

func _calculateScore(column Column) int {
	scoreType := 0
	scoreName := 1
	if slices.Contains(column.Constraint, "PRIMARY KEY") {
		scoreType = 3
		if column.Type == "uuid" {
			scoreType = 1
		}
	} else if slices.Contains(column.Constraint, "UNIQUE") {
		scoreType = 2
	}
	switch strings.ToLower(column.Name) {
	case "name":
		scoreName = 2
	case "label":
		scoreName = 2
	case "email":
		scoreName = 5
	}
	return scoreType * scoreName
}

func convertFromDB(val any) any {
	switch tmp := val.(type) {
	case []byte:
		return string(tmp)
	case time.Time:
		return tmp.UTC().Format("2006-01-02T15:04")
	}
	return val
}

func createFormElement(val any, column Column) FormElement {
	f := FormElement{
		Type: "text",
	}
	switch column.Type {
	case "timestamptz":
		f.Type = "datetime"
	case "bool":
		f.Type = "boolean"
	}
	f.Value = convertFromDB(val)

	f.Name = column.Name
	f.Required = !column.Nullable && !column.Default

	if strings.Contains(strings.ToLower(column.Name), "password") {
		f.Type = "password"
	}
	return f
}
