package user_account

import (
	"context"

	"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
	"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
	"github.com/huandu/go-sqlbuilder"
	"github.com/pkg/errors"
	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
)

// UserFindByAccount lists all the users for a given account ID.
func (repo *Repository) UserFindByAccount(ctx context.Context, claims auth.Claims, req UserFindByAccountRequest) (Users, error) {
	span, ctx := tracer.StartSpanFromContext(ctx, "internal.user_account.UserFindByAccount")
	defer span.Finish()

	v := webcontext.Validator()

	// Validate the request.
	err := v.StructCtx(ctx, req)
	if err != nil {
		return nil, err
	}

	/*
		SELECT
			id,
			first_name,
			last_name,
			name,
			email,
			timezone,
			account_id,
			status,
			roles,
			created_at,
			updated_at,
			archived_at FROM (
				SELECT
					u.id,
					u.first_name,
					u.last_name,
					concat(u.first_name, ' ',u.last_name) as name,
					u.email,
					u.timezone,
					ua.account_id,
					ua.status,
					ua.roles,
					CASE WHEN ua.created_at > u.created_at THEN ua.created_at ELSE u.created_at END AS created_at,
					CASE WHEN ua.updated_at > u.updated_at THEN ua.updated_at ELSE u.updated_at END AS updated_at,
					CASE WHEN ua.archived_at > u.archived_at THEN ua.archived_at ELSE u.archived_at END AS archived_at
				FROM users u
				JOIN users_accounts ua
					ON u.id = ua.user_id AND ua.account_id = 'df1a8a65-b00b-4640-9a64-66c1a355b17c'
				WHERE
					(u.archived_at IS NULL AND ua.archived_at IS NULL) AND
					account_id IN (SELECT account_id FROM users_accounts WHERE (account_id = ? OR user_id = ?))
			) res ORDER BY id asc

	*/

	subQuery := sqlbuilder.NewSelectBuilder().
		Select("u.id,u.first_name,u.last_name,concat(u.first_name, ' ',u.last_name) as name,u.email,u.timezone,ua.account_id,ua.status,ua.roles,"+
			"CASE WHEN ua.created_at > u.created_at THEN ua.created_at ELSE u.created_at END AS created_at,"+
			"CASE WHEN ua.updated_at > u.updated_at THEN ua.updated_at ELSE u.updated_at END AS updated_at,"+
			"CASE WHEN ua.archived_at > u.archived_at THEN ua.archived_at ELSE u.archived_at END AS archived_at").
		From(userTableName+" u").
		Join(userAccountTableName+" ua", "u.id = ua.user_id", "ua.account_id = '"+req.AccountID+"'")

	if !req.IncludeArchived {
		subQuery.Where(subQuery.And(
			subQuery.IsNull("u.archived_at"),
			subQuery.IsNull("ua.archived_at")))
	}

	if claims.Audience != "" || claims.Subject != "" {
		// Build select statement for users_accounts table
		authQuery := sqlbuilder.NewSelectBuilder().Select("account_id").From(userAccountTableName)

		var or []string
		if claims.Audience != "" {
			or = append(or, authQuery.Equal("account_id", claims.Audience))
		}
		if claims.Subject != "" {
			or = append(or, authQuery.Equal("user_id", claims.Subject))
		}

		// Append sub query
		if len(or) > 0 {
			authQuery.Where(authQuery.Or(or...))
			subQuery.Where(subQuery.In("account_id", authQuery))
		}
	}

	subQueryStr, queryArgs := subQuery.Build()

	query := sqlbuilder.NewSelectBuilder().
		Select("id,first_name,last_name,name,email,timezone,account_id,status,roles,created_at,updated_at,archived_at").
		From("(" + subQueryStr + ") res")
	if req.Where != "" {
		query.Where(query.And(req.Where))
	}
	if len(req.Order) > 0 {
		query.OrderBy(req.Order...)
	}
	if req.Limit != nil {
		query.Limit(int(*req.Limit))
	}
	if req.Offset != nil {
		query.Offset(int(*req.Offset))
	}

	queryStr, moreQueryArgs := query.Build()
	queryStr = repo.DbConn.Rebind(queryStr)

	queryArgs = append(queryArgs, moreQueryArgs...)

	// fetch all places from the db
	rows, err := repo.DbConn.QueryContext(ctx, queryStr, queryArgs...)
	if err != nil {
		err = errors.Wrapf(err, "query - %s", query.String())
		err = errors.WithMessage(err, "find users failed")
		return nil, err
	}

	// iterate over each row
	resp := []*User{}
	for rows.Next() {

		var (
			u   User
			err error
		)
		err = rows.Scan(&u.ID, &u.FirstName, &u.LastName, &u.Name, &u.Email, &u.Timezone, &u.AccountID, &u.Status,
			&u.Roles, &u.CreatedAt, &u.UpdatedAt, &u.ArchivedAt)
		if err != nil {
			err = errors.Wrapf(err, "query - %s", query.String())
			return nil, err
		}

		resp = append(resp, &u)
	}

	return resp, nil
}
