package postgres

import (
	"context"
	"fmt"
)

type MapExperienceRow struct {
	PlacePublicID            string
	PlaceName                string
	MapAssetPublicID         string
	MapAssetName             string
	MapCoverURL              *string
	MapSummary               *string
	TileBaseURL              *string
	TileMetaURL              *string
	EventPublicID            *string
	EventDisplayName         *string
	EventSummary             *string
	EventStatus              *string
	EventIsDefaultExperience bool
	EventShowInEventList     bool
	EventReleasePayloadJSON  *string
	EventPresentationID      *string
	EventPresentationName    *string
	EventPresentationType    *string
	EventPresentationSchema  *string
	EventContentBundleID     *string
	EventContentBundleName   *string
	EventContentEntryURL     *string
	EventContentAssetRootURL *string
	EventContentMetadataJSON *string
}

func (s *Store) ListMapExperienceRows(ctx context.Context, limit int) ([]MapExperienceRow, error) {
	if limit <= 0 || limit > 200 {
		limit = 50
	}
	rows, err := s.pool.Query(ctx, `
		SELECT
			p.place_public_id,
			p.name,
			ma.map_asset_public_id,
			ma.name,
			COALESCE(ma.cover_url, p.cover_url) AS cover_url,
			COALESCE(ma.description, p.description) AS summary,
			tr.tile_base_url,
			tr.meta_url,
			e.event_public_id,
			e.display_name,
			e.summary,
			e.status,
			COALESCE(e.is_default_experience, false),
			COALESCE(e.show_in_event_list, true),
			er.payload_jsonb::text,
			ep.presentation_public_id,
			ep.name,
			ep.presentation_type,
			ep.schema_jsonb::text,
			cb.content_bundle_public_id,
			cb.name,
			cb.entry_url,
			cb.asset_root_url,
			cb.metadata_jsonb::text
		FROM map_assets ma
		JOIN places p ON p.id = ma.place_id
		LEFT JOIN tile_releases tr ON tr.id = ma.current_tile_release_id
		LEFT JOIN map_runtime_bindings mrb
			ON mrb.map_asset_id = ma.id
			AND mrb.status = 'active'
		LEFT JOIN event_releases er
			ON er.runtime_binding_id = mrb.id
			AND er.status = 'published'
		LEFT JOIN events e
			ON e.current_release_id = er.id
			AND e.status = 'active'
		LEFT JOIN event_presentations ep ON ep.id = er.presentation_id
		LEFT JOIN content_bundles cb ON cb.id = er.content_bundle_id
		WHERE ma.status = 'active'
		  AND (e.id IS NULL OR e.show_in_event_list = true)
		ORDER BY p.name ASC, ma.name ASC, COALESCE(e.is_default_experience, false) DESC, e.display_name ASC
		LIMIT $1
	`, limit)
	if err != nil {
		return nil, fmt.Errorf("list map experience rows: %w", err)
	}
	defer rows.Close()

	items := make([]MapExperienceRow, 0, limit)
	for rows.Next() {
		var item MapExperienceRow
		if err := rows.Scan(
			&item.PlacePublicID,
			&item.PlaceName,
			&item.MapAssetPublicID,
			&item.MapAssetName,
			&item.MapCoverURL,
			&item.MapSummary,
			&item.TileBaseURL,
			&item.TileMetaURL,
			&item.EventPublicID,
			&item.EventDisplayName,
			&item.EventSummary,
			&item.EventStatus,
			&item.EventIsDefaultExperience,
			&item.EventShowInEventList,
			&item.EventReleasePayloadJSON,
			&item.EventPresentationID,
			&item.EventPresentationName,
			&item.EventPresentationType,
			&item.EventPresentationSchema,
			&item.EventContentBundleID,
			&item.EventContentBundleName,
			&item.EventContentEntryURL,
			&item.EventContentAssetRootURL,
			&item.EventContentMetadataJSON,
		); err != nil {
			return nil, fmt.Errorf("scan map experience row: %w", err)
		}
		items = append(items, item)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate map experience rows: %w", err)
	}
	return items, nil
}

func (s *Store) ListMapExperienceRowsByMapPublicID(ctx context.Context, mapAssetPublicID string) ([]MapExperienceRow, error) {
	rows, err := s.pool.Query(ctx, `
		SELECT
			p.place_public_id,
			p.name,
			ma.map_asset_public_id,
			ma.name,
			COALESCE(ma.cover_url, p.cover_url) AS cover_url,
			COALESCE(ma.description, p.description) AS summary,
			tr.tile_base_url,
			tr.meta_url,
			e.event_public_id,
			e.display_name,
			e.summary,
			e.status,
			COALESCE(e.is_default_experience, false),
			COALESCE(e.show_in_event_list, true),
			er.payload_jsonb::text,
			ep.presentation_public_id,
			ep.name,
			ep.presentation_type,
			ep.schema_jsonb::text,
			cb.content_bundle_public_id,
			cb.name,
			cb.entry_url,
			cb.asset_root_url,
			cb.metadata_jsonb::text
		FROM map_assets ma
		JOIN places p ON p.id = ma.place_id
		LEFT JOIN tile_releases tr ON tr.id = ma.current_tile_release_id
		LEFT JOIN map_runtime_bindings mrb
			ON mrb.map_asset_id = ma.id
			AND mrb.status = 'active'
		LEFT JOIN event_releases er
			ON er.runtime_binding_id = mrb.id
			AND er.status = 'published'
		LEFT JOIN events e
			ON e.current_release_id = er.id
			AND e.status = 'active'
		LEFT JOIN event_presentations ep ON ep.id = er.presentation_id
		LEFT JOIN content_bundles cb ON cb.id = er.content_bundle_id
		WHERE ma.map_asset_public_id = $1
		  AND ma.status = 'active'
		  AND (e.id IS NULL OR e.show_in_event_list = true)
		ORDER BY COALESCE(e.is_default_experience, false) DESC, e.display_name ASC
	`, mapAssetPublicID)
	if err != nil {
		return nil, fmt.Errorf("list map experience rows by map public id: %w", err)
	}
	defer rows.Close()

	items := make([]MapExperienceRow, 0, 8)
	for rows.Next() {
		var item MapExperienceRow
		if err := rows.Scan(
			&item.PlacePublicID,
			&item.PlaceName,
			&item.MapAssetPublicID,
			&item.MapAssetName,
			&item.MapCoverURL,
			&item.MapSummary,
			&item.TileBaseURL,
			&item.TileMetaURL,
			&item.EventPublicID,
			&item.EventDisplayName,
			&item.EventSummary,
			&item.EventStatus,
			&item.EventIsDefaultExperience,
			&item.EventShowInEventList,
			&item.EventReleasePayloadJSON,
			&item.EventPresentationID,
			&item.EventPresentationName,
			&item.EventPresentationType,
			&item.EventPresentationSchema,
			&item.EventContentBundleID,
			&item.EventContentBundleName,
			&item.EventContentEntryURL,
			&item.EventContentAssetRootURL,
			&item.EventContentMetadataJSON,
		); err != nil {
			return nil, fmt.Errorf("scan map experience detail row: %w", err)
		}
		items = append(items, item)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate map experience detail rows: %w", err)
	}
	return items, nil
}
