package resource

import (
	"context"
	"net/http"
	"time"

	"github.com/Gleipnir-Technology/bob"
	"github.com/Gleipnir-Technology/bob/dialect/psql"
	"github.com/Gleipnir-Technology/bob/dialect/psql/sm"
	"source.gleipnir.technology/Gleipnir/nidus-sync/db"
	nhttp "source.gleipnir.technology/Gleipnir/nidus-sync/http"
	"source.gleipnir.technology/Gleipnir/nidus-sync/platform"
	"source.gleipnir.technology/Gleipnir/nidus-sync/platform/types"
	//"github.com/aarondl/opt/null"
	"github.com/gorilla/mux"
	"github.com/stephenafamo/scan"
)

type reviewTaskR struct {
	router *mux.Router
}

func ReviewTask(r *mux.Router) *reviewTaskR {
	return &reviewTaskR{
		router: r,
	}
}

type reviewTask struct {
	Address  types.Address  `json:"address"`
	Created  time.Time      `json:"created"`
	Creator  platform.User  `json:"creator"`
	ID       int32          `json:"id"`
	Pool     reviewTaskPool `json:"pool"`
	Reviewed *time.Time     `json:"addressed"`
	Reviewer *platform.User `json:"addressor"`
}
type reviewTaskPool struct {
	Condition string         `json:"condition"`
	Location  types.Location `json:"location"`
	Site      types.Site     `json:"site"`
}
type contentListReviewTask struct {
	Tasks []reviewTask `json:"tasks"`
	Total int32        `json:"total"`
}

func (res *reviewTaskR) List(ctx context.Context, r *http.Request, user platform.User, query QueryParams) (*contentListReviewTask, *nhttp.ErrorWithStatus) {
	limit := 20
	if query.Limit != nil {
		limit = *query.Limit
	}
	type _RowTotal struct {
		Total int32 `db:"total"`
	}
	row_total, err := bob.One(ctx, db.PGInstance.BobDB, psql.Select(
		sm.Columns(
			"COUNT(*) AS total",
		),
		sm.From("review_task"),
		sm.Where(psql.Quote("review_task", "organization_id").EQ(psql.Arg(user.Organization.ID))),
		sm.Where(psql.Quote("review_task", "reviewed").IsNull()),
	), scan.StructMapper[_RowTotal]())
	if err != nil {
		return nil, nhttp.NewError("failed to total count: %w", err)
	}

	type _Row struct {
		Address    types.Address `db:"address"`
		Condition  string        `db:"condition"`
		Created    time.Time     `db:"created"`
		CreatorID  int32         `db:"creator_id"`
		ID         int32         `db:"id"`
		Latitude   float64       `db:"latitude"`
		Longitude  float64       `db:"longitude"`
		Reviewed   *time.Time    `db:"reviewed"`
		ReviewerID *int32        `db:"reviewer_id"`
		SiteID     int32         `db:"site_id"`
		Title      string        `db:"title"`
		Type       string        `db:"type"`
	}
	rows, err := bob.All(ctx, db.PGInstance.BobDB, psql.Select(
		sm.Columns(
			"feature_pool.condition AS condition",
			"review_task.created AS created",
			"review_task.creator_id AS creator_id",
			"review_task.id AS id",
			"review_task.reviewed AS reviewed",
			"review_task.reviewer_id AS reviewer_id",
			"address.country AS \"address.country\"",
			"address.locality AS \"address.locality\"",
			"address.number_ AS \"address.number_\"",
			"address.postal_code AS \"address.postal_code\"",
			"address.region AS \"address.region\"",
			"address.street AS \"address.street\"",
			"address.unit AS \"address.unit\"",
			"ST_Y(address.location) AS latitude",
			"ST_X(address.location) AS longitude",
			"site.id AS site_id",
		),
		sm.From("review_task_pool"),
		sm.InnerJoin("feature_pool").OnEQ(
			psql.Quote("review_task_pool", "feature_pool_id"),
			psql.Quote("feature_pool", "feature_id"),
		),
		sm.InnerJoin("review_task").OnEQ(
			psql.Quote("review_task_pool", "review_task_id"),
			psql.Quote("review_task", "id"),
		),
		sm.InnerJoin("feature").OnEQ(
			psql.Quote("feature_pool", "feature_id"),
			psql.Quote("feature", "id"),
		),
		sm.InnerJoin("site").On(
			psql.Quote("feature", "site_id").EQ(psql.Quote("site", "id")),
		),
		sm.InnerJoin("address").OnEQ(
			psql.Quote("site", "address_id"),
			psql.Quote("address", "id"),
		),
		sm.Where(psql.Quote("review_task", "organization_id").EQ(psql.Arg(user.Organization.ID))),
		sm.Where(psql.Quote("review_task", "reviewed").IsNull()),
		sm.Limit(limit),
	), scan.StructMapper[_Row]())
	if err != nil {
		return nil, nhttp.NewError("failed to get review tasks: %w", err)
	}
	users_by_id, err := platform.UsersByOrg(ctx, user.Organization)
	if err != nil {
		return nil, nhttp.NewError("users by id: %w", err)
	}
	site_ids := make([]int32, len(rows))
	for i, row := range rows {
		site_ids[i] = row.SiteID
	}
	sites_by_id, err := platform.SitesByID(ctx, site_ids)
	if err != nil {
		return nil, nhttp.NewError("sites by id: %w", err)
	}
	tasks := make([]reviewTask, len(rows))
	for i, row := range rows {
		site, ok := sites_by_id[row.SiteID]
		if !ok {
			return nil, nhttp.NewError("no site %d", row.SiteID)
		}
		tasks[i] = reviewTask{
			Address: row.Address,
			Created: row.Created,
			Creator: *users_by_id[row.CreatorID],
			ID:      row.ID,
			Pool: reviewTaskPool{
				Condition: row.Condition,
				Location: types.Location{
					Latitude:  row.Latitude,
					Longitude: row.Longitude,
				},
				Site: types.SiteFromModel(site),
			},
			Reviewed: row.Reviewed,
			Reviewer: userOrNil(users_by_id, row.ReviewerID),
		}
	}
	return &contentListReviewTask{
		Tasks: tasks,
		Total: row_total.Total,
	}, nil
}
func userOrNil(usersByID map[int32]*platform.User, id *int32) *platform.User {
	if id == nil {
		return nil
	}
	u, ok := usersByID[*id]
	if !ok {
		return nil
	}
	return u
}
