-- Finance monthly-result backfill and booking-source enrichment support v1.0
-- PostgreSQL 15+
-- Target database: booking_test (connect to that database before running).
-- This migration is additive; do not edit applied migration 001 in place.

BEGIN;

ALTER TABLE finance.daily_versions
    ADD COLUMN ingestion_mode text NOT NULL DEFAULT 'opera_xml';

ALTER TABLE finance.daily_versions
    ADD CONSTRAINT daily_versions_ingestion_mode_valid CHECK (
        ingestion_mode IN ('opera_xml', 'monthly_backfill')
    );

ALTER TABLE finance.daily_versions
    DROP CONSTRAINT daily_version_active_shape;

ALTER TABLE finance.daily_versions
    ADD CONSTRAINT daily_version_active_shape CHECK (
        version_status <> 'active'
        OR (
            business_date IS NOT NULL
            AND validated_at IS NOT NULL
            AND activated_at IS NOT NULL
            AND failure_code IS NULL
            AND (
                (
                    ingestion_mode = 'opera_xml'
                    AND daily_report_file_id IS NOT NULL
                    AND result_json_file_id IS NOT NULL
                )
                OR ingestion_mode = 'monthly_backfill'
            )
        )
    );

CREATE UNIQUE INDEX daily_versions_source_date_mode_unique
    ON finance.daily_versions (
        source_file_id,
        business_date,
        ingestion_mode
    );

COMMENT ON COLUMN finance.daily_versions.ingestion_mode IS
  'opera_xml for normal deterministic processing; monthly_backfill only for an explicitly approved processed-workbook backfill.';

ALTER TABLE finance.daily_records
    ADD COLUMN source_worksheet text,
    ADD COLUMN source_row_no integer,
    ADD COLUMN booking_source_row_id bigint,
    ADD COLUMN booking_source_match_status text NOT NULL DEFAULT 'not_checked';

ALTER TABLE finance.daily_records
    ADD CONSTRAINT daily_records_source_worksheet_valid CHECK (
        source_worksheet IS NULL OR btrim(source_worksheet) <> ''
    ),
    ADD CONSTRAINT daily_records_source_row_no_valid CHECK (
        source_row_no IS NULL OR source_row_no > 1
    ),
    ADD CONSTRAINT daily_records_source_coordinate_shape CHECK (
        (source_worksheet IS NULL AND source_row_no IS NULL)
        OR (source_worksheet IS NOT NULL AND source_row_no IS NOT NULL)
    ),
    ADD CONSTRAINT daily_records_booking_source_status_valid CHECK (
        booking_source_match_status IN (
            'matched',
            'unmatched',
            'missing_group_code',
            'conflict',
            'not_checked'
        )
    ),
    ADD CONSTRAINT daily_records_booking_source_group_fk
        FOREIGN KEY (booking_source_row_id, group_code_key)
        REFERENCES booking.booking_source_rows(id, group_code_key),
    ADD CONSTRAINT daily_records_booking_source_match_shape CHECK (
        (
            booking_source_match_status = 'matched'
            AND group_code_key IS NOT NULL
            AND booking_source_row_id IS NOT NULL
        )
        OR (
            booking_source_match_status IN ('unmatched', 'conflict')
            AND group_code_key IS NOT NULL
            AND booking_source_row_id IS NULL
        )
        OR (
            booking_source_match_status = 'missing_group_code'
            AND group_code_key IS NULL
            AND booking_source_row_id IS NULL
        )
        OR (
            booking_source_match_status = 'not_checked'
            AND booking_source_row_id IS NULL
        )
    );

CREATE UNIQUE INDEX daily_records_source_coordinate_unique
    ON finance.daily_records (
        daily_version_id,
        source_worksheet,
        source_row_no
    )
    WHERE source_worksheet IS NOT NULL;

CREATE INDEX daily_records_booking_source_row_idx
    ON finance.daily_records (booking_source_row_id)
    WHERE booking_source_row_id IS NOT NULL;

CREATE INDEX daily_records_booking_source_status_idx
    ON finance.daily_records (booking_source_match_status);

COMMENT ON COLUMN finance.daily_records.source_worksheet IS
  'Exact source worksheet for processed-workbook backfills; null for direct XML rows.';

COMMENT ON COLUMN finance.daily_records.source_row_no IS
  'One-based source worksheet row number; null for direct XML rows.';

COMMENT ON COLUMN finance.daily_records.booking_source_row_id IS
  'Accepted booking attachment source row selected by exact normalized RES_COMMENT Group Code.';

COMMENT ON COLUMN finance.daily_records.booking_source_match_status IS
  'Source-layer booking lookup status, separate from Agent parse-version matching.';

ALTER TABLE finance.report_versions
    ADD COLUMN as_of_date date NOT NULL,
    ADD COLUMN processor_version text NOT NULL,
    ADD COLUMN rule_set_sha256 character(64),
    ADD COLUMN result_schema_version text NOT NULL DEFAULT '1.0';

ALTER TABLE finance.report_versions
    ADD CONSTRAINT report_versions_as_of_date_valid CHECK (
        as_of_date BETWEEN period_start AND period_end
    ),
    ADD CONSTRAINT report_versions_processor_version_valid CHECK (
        btrim(processor_version) <> ''
    ),
    ADD CONSTRAINT report_versions_rule_set_sha256_valid CHECK (
        rule_set_sha256 IS NULL
        OR rule_set_sha256 ~ '^[0-9a-f]{64}$'
    ),
    ADD CONSTRAINT report_versions_result_schema_version_valid CHECK (
        btrim(result_schema_version) <> ''
    );

COMMENT ON COLUMN finance.report_versions.as_of_date IS
  'Latest business date included in this report artifact, distinct from its natural reporting period end.';

COMMENT ON COLUMN finance.report_versions.processor_version IS
  'Deterministic report generator or approved backfill adapter version.';

CREATE TABLE finance.report_booking_sources (
    report_version_id bigint NOT NULL
        REFERENCES finance.report_versions(id),
    group_code_key text NOT NULL CHECK (
        group_code_key = upper(btrim(group_code_key))
        AND group_code_key <> ''
    ),
    booking_source_row_id bigint NOT NULL,
    PRIMARY KEY (report_version_id, group_code_key),
    CONSTRAINT report_booking_source_row_fk
        FOREIGN KEY (booking_source_row_id, group_code_key)
        REFERENCES booking.booking_source_rows(id, group_code_key)
);

COMMENT ON TABLE finance.report_booking_sources IS
  'Freezes booking attachment source rows used by a report before Agent parse versions exist or are required.';

CREATE OR REPLACE VIEW finance.v_company_report_source AS
SELECT
    current_version.business_date,
    records.id AS daily_record_id,
    records.channel_key,
    records.company_key,
    records.company_name,
    records.block_code,
    records.group_code_key,
    records.res_comment,
    booking_source.hotel_raw AS type_of_room,
    records.no_of_rooms AS actual_room_quantity,
    records.arrival,
    records.departure,
    records.nights,
    records.real_price,
    records.total_price,
    records.booking_parse_version_id,
    records.booking_match_status
FROM finance.current_daily_versions AS current_version
JOIN finance.daily_records AS records
  ON records.daily_version_id = current_version.daily_version_id
LEFT JOIN booking.booking_source_rows AS booking_source
  ON booking_source.id = records.booking_source_row_id
WHERE records.outcome = 'retained';

CREATE VIEW finance.v_channel_booking_enriched AS
SELECT
    current_version.business_date,
    records.daily_version_id,
    records.id AS daily_record_id,
    records.source_worksheet,
    records.source_row_no,
    records.channel_key,
    records.company_key,
    records.company_name,
    records.rate_code,
    records.normalized_rate_code,
    records.block_code,
    records.group_code_key,
    records.res_comment,
    records.room_category_label AS opera_room_category_label,
    booking_source.hotel_raw AS booking_type_of_room_raw,
    records.booking_source_row_id,
    records.booking_source_match_status,
    records.adults,
    records.children,
    records.no_of_rooms,
    records.arrival,
    records.departure,
    records.nights,
    records.effective_rate_amount,
    records.real_price,
    records.total_price,
    records.kb_amount,
    records.pricing_method
FROM finance.current_daily_versions AS current_version
JOIN finance.daily_records AS records
  ON records.daily_version_id = current_version.daily_version_id
LEFT JOIN booking.booking_source_rows AS booking_source
  ON booking_source.id = records.booking_source_row_id
WHERE records.outcome = 'retained';

COMMENT ON VIEW finance.v_company_report_source IS
  'Privacy-minimized company-report facts; type_of_room is the matched booking attachment source text, never Opera ROOM_CATEGORY_LABEL.';

COMMENT ON VIEW finance.v_channel_booking_enriched IS
  'Privacy-minimized active channel facts with Opera room category and booking original type_of_room kept as separate fields.';

COMMIT;
