package rmo

import (
	"context"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/Gleipnir-Technology/bob"
	"github.com/Gleipnir-Technology/bob/dialect/psql"
	"github.com/Gleipnir-Technology/bob/dialect/psql/sm"
	"github.com/Gleipnir-Technology/nidus-sync/db"
	"github.com/Gleipnir-Technology/nidus-sync/db/models"
	"github.com/Gleipnir-Technology/nidus-sync/html"
	"github.com/google/uuid"
	"github.com/gorilla/mux"
	//"github.com/rs/zerolog/log"
	"github.com/stephenafamo/scan"
	"golang.org/x/text/cases"
	"golang.org/x/text/language"
)

type ContentStatus struct {
	District *ContentDistrict
	Error    string
	ReportID string
	URL      ContentURL
}
type ContentStatusByID struct {
	District *ContentDistrict
	Report   Report
	Timeline []TimelineEntry
	URL      ContentURL
}
type DetailEntry struct {
	Name  string
	Value string
}
type Report struct {
	Address    string
	Comments   string
	Created    time.Time
	Details    []DetailEntry
	ID         string
	ImageCount int
	Location   string // GeoJSON
	Status     string
	Type       string
}
type TimelineEntry struct {
	At     time.Time
	Detail string
	Title  string
}

func formatReportID(s string) string {
	// truncate down if too long
	if len(s) > 12 {
		s = s[:12]
	}

	// If less than 4 characters, return as is
	if len(s) < 4 {
		return s
	}

	// If at least 8 characters, add hyphens at positions 4 and 8
	if len(s) >= 8 {
		return s[0:4] + "-" + s[4:8] + "-" + s[8:]
	}

	// If at least 4 characters but less than 8, add hyphen only at position 4
	return s[0:4] + "-" + s[4:]
}

func getStatus(w http.ResponseWriter, r *http.Request) {
	report_id_str := r.URL.Query().Get("report")
	content := ContentStatus{
		Error:    "",
		ReportID: "",
		URL:      makeContentURL(nil),
	}
	if report_id_str == "" {
		html.RenderOrError(w, "rmo/status.html", content)
		return
	}
	report_id := sanitizeReportID(report_id_str)
	report_id_str = formatReportID(report_id)
	//some_report, e := report.FindSomeReport(r.Context(), report_id)
	content.Error = "Sorry, we can't find that report"
	html.RenderOrError(w, "rmo/status.html", content)
}
func contentFromReport(ctx context.Context, report *models.PublicreportReport) (result ContentStatusByID, err error) {
	org, err := models.FindOrganization(ctx, db.PGInstance.BobDB, report.OrganizationID)
	if err != nil {
		return result, fmt.Errorf("Failed to get district information: %w", err)
	}

	type _Row struct {
		ID               int32     `db:"id"`
		ContentType      string    `db:"content_type"`
		Created          time.Time `db:"created"`
		Location         string    `db:"location"`
		LocationJSON     string    `db:"location_json"`
		ResolutionX      int32     `db:"resolution_x"`
		ResolutionY      int32     `db:"resolution_y"`
		StorageUUID      uuid.UUID `db:"storage_uuid"`
		StorageSize      int32     `db:"storage_size"`
		UploadedFilename string    `db:"uploaded_filename"`
	}
	images, err := bob.All(ctx, db.PGInstance.BobDB, psql.Select(
		sm.Columns(
			"id",
			"content_type",
			"created",
			"location",
			"COALESCE(ST_AsGeoJSON(location), '{}') AS location_json",
			"resolution_x",
			"resolution_y",
			"storage_uuid",
			"storage_size",
			"uploaded_filename",
		),
		sm.From("publicreport.image"),
		sm.InnerJoin("publicreport.report_image").OnEQ(
			psql.Quote("publicreport", "image", "id"),
			psql.Quote("publicreport", "report_image", "image_id"),
		),
		sm.Where(
			psql.Quote("publicreport", "report_image", "report_id").EQ(psql.Arg(report.ID)),
		),
	), scan.StructMapper[_Row]())
	if err != nil {
		return result, fmt.Errorf("Failed to get images: %w", err)
	}
	result.District = newContentDistrict(org)
	result.Report.ID = report.PublicID
	result.Report.Address = report.AddressRaw
	result.Report.Created = report.Created
	result.Report.ImageCount = len(images)
	result.Report.Status = cases.Title(language.AmericanEnglish).String(report.Status.String())
	result.Timeline = []TimelineEntry{
		TimelineEntry{
			At:     report.Created,
			Detail: "Initial report was submitted",
			Title:  "Created",
		},
	}

	type LocationGeoJSON struct {
		Location string
	}
	location, err := bob.One(ctx, db.PGInstance.BobDB, psql.Select(
		sm.Columns(
			psql.F("ST_AsGeoJSON", "location"),
		),
		sm.From("publicreport.report"),
		sm.Where(psql.Quote("id").EQ(psql.Arg(report.ID))),
	), scan.SingleColumnMapper[string])
	if err != nil {
		return result, fmt.Errorf("Failed to query location of report %d: %w", report.ID, err)
	}
	result.Report.Location = location
	nuisance, err := models.PublicreportNuisances.Query(
		models.SelectWhere.PublicreportNuisances.ReportID.EQ(report.ID),
	).One(ctx, db.PGInstance.BobDB)
	if err == nil {
		result.Report.Type = "Mosquito Nuisance"
		addContentFromNuisance(&result, nuisance)
	}
	water, err := models.PublicreportWaters.Query(
		models.SelectWhere.PublicreportWaters.ReportID.EQ(report.ID),
	).One(ctx, db.PGInstance.BobDB)
	if err == nil {
		result.Report.Type = "Standing Water"
		addContentFromWater(&result, water)
	}
	return result, nil
}
func addContentFromNuisance(result *ContentStatusByID, nuisance *models.PublicreportNuisance) {
	result.Report.Type = "Mosquito Nuisance"
	result.Report.Details = []DetailEntry{
		DetailEntry{
			Name:  "Active early morning (5a-8a)?",
			Value: strconv.FormatBool(nuisance.TodEarly),
		},
		DetailEntry{
			Name:  "Active daytime (8a-5p)?",
			Value: strconv.FormatBool(nuisance.TodDay),
		},
		DetailEntry{
			Name:  "Active evening (5p-9p)?",
			Value: strconv.FormatBool(nuisance.TodEvening),
		},
		DetailEntry{
			Name:  "Active night (9p-5a)?",
			Value: strconv.FormatBool(nuisance.TodNight),
		},
		DetailEntry{
			Name:  "Duration",
			Value: nuisance.Duration.String(),
		},
		DetailEntry{
			Name:  "Active in backyard?",
			Value: strconv.FormatBool(nuisance.IsLocationBackyard),
		},
		DetailEntry{
			Name:  "Active in frontyard?",
			Value: strconv.FormatBool(nuisance.IsLocationFrontyard),
		},
		DetailEntry{
			Name:  "Active in garden?",
			Value: strconv.FormatBool(nuisance.IsLocationGarden),
		},
		DetailEntry{
			Name:  "Active in other location?",
			Value: strconv.FormatBool(nuisance.IsLocationOther),
		},
		DetailEntry{
			Name:  "Active in pool area?",
			Value: strconv.FormatBool(nuisance.IsLocationPool),
		},
		DetailEntry{
			Name:  "Stagnant Water",
			Value: strconv.FormatBool(nuisance.SourceStagnant),
		},
		DetailEntry{
			Name:  "Container",
			Value: strconv.FormatBool(nuisance.SourceContainer),
		},
		DetailEntry{
			Name:  "Sprinklers & Gutters",
			Value: strconv.FormatBool(nuisance.SourceGutter),
		},
	}
}
func addContentFromWater(result *ContentStatusByID, water *models.PublicreportWater) {
	result.Report.Details = []DetailEntry{
		DetailEntry{
			Name:  "Has a gate that affects access?",
			Value: strconv.FormatBool(water.AccessGate),
		},
		DetailEntry{
			Name:  "Has dog that affects access?",
			Value: strconv.FormatBool(water.AccessDog),
		},
		DetailEntry{
			Name:  "Has a fence that affects access?",
			Value: strconv.FormatBool(water.AccessFence),
		},
		DetailEntry{
			Name:  "Has a locked entrace that affects access?",
			Value: strconv.FormatBool(water.AccessLocked),
		},
		DetailEntry{
			Name:  "Reporter observed larvae (wigglers)?",
			Value: strconv.FormatBool(water.HasLarvae),
		},
		DetailEntry{
			Name:  "Reporter observed pupae (tumblers)?",
			Value: strconv.FormatBool(water.HasPupae),
		},
		DetailEntry{
			Name:  "Reporter observed adult mosquitoes?",
			Value: strconv.FormatBool(water.HasAdult),
		},
	}
}

func getStatusByID(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	report_id := vars["report_id"]
	ctx := r.Context()

	report, err := models.PublicreportReports.Query(
		models.SelectWhere.PublicreportReports.PublicID.EQ(report_id),
	).One(ctx, db.PGInstance.BobDB)
	if err != nil {
		respondError(w, "Failed to find report", err, http.StatusBadRequest)
		return
	}
	content, err := contentFromReport(ctx, report)
	if err != nil {
		respondError(w, "Failed to generate report content", err, http.StatusInternalServerError)
		return
	}
	content.URL = makeContentURL(nil)
	html.RenderOrError(
		w,
		"rmo/status-by-id.html",
		content,
	)
}

func sanitizeReportID(r string) string {
	result := ""
	for _, char := range r {
		if char != '-' {
			result += string(char)
		}
	}
	return strings.ToUpper(result)
}
