
-- DONE
-- ============================================================================
-- PART 0.5: USER AUTHENTICATION TABLE
-- ============================================================================
-- This replaces the Supabase `auth.users` table.
CREATE TABLE IF NOT EXISTS public.users (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    email TEXT NOT NULL UNIQUE,
    password_hash TEXT NOT NULL,
    refresh_token TEXT, -- Stores the long-lived refresh token for re-authentication.
    created_at TIMESTAMPTZ DEFAULT now() NOT NULL
);
COMMENT ON TABLE public.users IS 'Stores user authentication information, replacing Supabase auth.';

-- Add an index on the refresh_token for faster lookups when refreshing tokens.
CREATE INDEX IF NOT EXISTS idx_users_refresh_token ON public.users(refresh_token);

-- 0. Create a table for public user profiles.
-- This table is linked to the auth.users table and stores non-sensitive user data.
CREATE TABLE IF NOT EXISTS public.profiles (
  id UUID PRIMARY KEY REFERENCES public.users(id) ON DELETE CASCADE,
  updated_at TIMESTAMPTZ,
  full_name TEXT,
  avatar_url TEXT,
  preferences JSONB,
  role TEXT CHECK (role IN ('admin', 'user'))
);
COMMENT ON TABLE public.profiles IS 'Stores public-facing user data, linked to the private auth.users table.';

-- DONE
-- 1. Create the 'stores' table for normalized store data.
CREATE TABLE IF NOT EXISTS public.stores (
  id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
  created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
  name TEXT NOT NULL UNIQUE,
  logo_url TEXT
);
COMMENT ON TABLE public.stores IS 'Stores metadata for grocery store chains (e.g., Safeway, Kroger).';

-- DONE
-- 2. Create the 'categories' table for normalized category data.
CREATE TABLE IF NOT EXISTS public.categories (
  id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
  name TEXT NOT NULL UNIQUE
);
COMMENT ON TABLE public.categories IS 'Stores a predefined list of grocery item categories (e.g., ''Fruits & Vegetables'', ''Dairy & Eggs'').';

-- DONE
-- 3. Create the 'flyers' table with its full, final schema.
CREATE TABLE IF NOT EXISTS public.flyers (
  id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
  created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
  file_name TEXT NOT NULL,
  image_url TEXT NOT NULL,
  checksum TEXT UNIQUE,
  store_id BIGINT REFERENCES public.stores(id),
  valid_from DATE,
  valid_to DATE,
  store_address TEXT
);
COMMENT ON TABLE public.flyers IS 'Stores metadata for each processed flyer, linking it to a store and its validity period.';

-- DONE
-- 4. Create the 'master_grocery_items' table. This is the master dictionary.
CREATE TABLE IF NOT EXISTS public.master_grocery_items (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
    name TEXT NOT NULL UNIQUE,
    category_id BIGINT REFERENCES public.categories(id)
);
COMMENT ON TABLE public.master_grocery_items IS 'The master dictionary of canonical grocery items. Each item has a unique name and is linked to a category.';

-- DONE
-- 5. Create the 'user_watched_items' table. This links to the master list.
CREATE TABLE IF NOT EXISTS public.user_watched_items (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    user_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE,
    master_item_id BIGINT NOT NULL REFERENCES public.master_grocery_items(id) ON DELETE CASCADE,
    created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
    UNIQUE(user_id, master_item_id)
);
COMMENT ON TABLE public.user_watched_items IS 'A linking table that represents a user''s personal watchlist of grocery items.';

-- DONE
-- 6. Create the 'flyer_items' table with its full, final schema.
CREATE TABLE IF NOT EXISTS public.flyer_items (
  id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
  created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
  flyer_id BIGINT REFERENCES public.flyers(id) ON DELETE CASCADE,
  item TEXT NOT NULL,
  price_display TEXT NOT NULL,
  price_in_cents INTEGER,
  quantity_num NUMERIC,
  quantity TEXT NOT NULL,
  category_id BIGINT REFERENCES public.categories(id),
  category_name TEXT, -- Denormalized for easier display
  unit_price JSONB,
  master_item_id BIGINT REFERENCES public.master_grocery_items(id),
  product_id BIGINT -- Future use for specific product linking
);
COMMENT ON TABLE public.flyer_items IS 'Stores individual items extracted from a specific flyer.';

-- DONE
-- 7. Create a table for user-defined alerts on watched items.
CREATE TABLE IF NOT EXISTS public.user_alerts (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    user_watched_item_id BIGINT NOT NULL REFERENCES public.user_watched_items(id) ON DELETE CASCADE,
    alert_type TEXT NOT NULL CHECK (alert_type IN ('PRICE_BELOW', 'PERCENT_OFF_AVERAGE')),
    threshold_value NUMERIC NOT NULL,
    is_active BOOLEAN DEFAULT true NOT NULL,
    created_at TIMESTAMPTZ DEFAULT now() NOT NULL
);
COMMENT ON TABLE public.user_alerts IS 'Stores user-configured alert rules for their watched items.';
COMMENT ON COLUMN public.user_alerts.alert_type IS 'The condition that triggers the alert, e.g., ''PRICE_BELOW''.';
COMMENT ON COLUMN public.user_alerts.threshold_value IS 'The numeric threshold for the alert condition (e.g., price in cents, or percentage).';

-- DONE
-- 8. Create a table to store notifications for users.
CREATE TABLE IF NOT EXISTS public.notifications (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    user_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE,
    content TEXT NOT NULL,
    link_url TEXT,
    is_read BOOLEAN DEFAULT false NOT NULL,
    created_at TIMESTAMPTZ DEFAULT now() NOT NULL
);
COMMENT ON TABLE public.notifications IS 'A central log of notifications generated for users, such as price alerts.';
COMMENT ON COLUMN public.notifications.content IS 'The notification message displayed to the user.';
COMMENT ON COLUMN public.notifications.link_url IS 'A URL to navigate to when the notification is clicked.';

-- DONE
-- 9. Create a table for aggregated, historical price data for master items.
CREATE TABLE IF NOT EXISTS public.item_price_history (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    master_item_id BIGINT NOT NULL REFERENCES public.master_grocery_items(id) ON DELETE CASCADE,
    summary_date DATE NOT NULL,
    min_price_in_cents INTEGER,
    max_price_in_cents INTEGER,
    avg_price_in_cents INTEGER,
    data_points_count INTEGER DEFAULT 0 NOT NULL,
    UNIQUE(master_item_id, summary_date)
);
COMMENT ON TABLE public.item_price_history IS 'Serves as a summary table to speed up charting and analytics.';
COMMENT ON COLUMN public.item_price_history.summary_date IS 'The date for which the price data is summarized.';
COMMENT ON COLUMN public.item_price_history.min_price_in_cents IS 'The lowest price found for this item on this day, in cents,';
COMMENT ON COLUMN public.item_price_history.max_price_in_cents IS 'The highest price found for this item on this day, in cents.';
COMMENT ON COLUMN public.item_price_history.avg_price_in_cents IS 'The average price found for this item on this day, in cents.';
COMMENT ON COLUMN public.item_price_history.data_points_count IS 'How many data points were used for this summary.';


-- DONE
-- 10. Create a table to map various names to a single master grocery item.
CREATE TABLE IF NOT EXISTS public.master_item_aliases (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    master_item_id BIGINT NOT NULL REFERENCES public.master_grocery_items(id) ON DELETE CASCADE,
    alias TEXT NOT NULL UNIQUE
);
COMMENT ON TABLE public.master_item_aliases IS 'Stores synonyms or alternative names for master items to improve matching.';
COMMENT ON COLUMN public.master_item_aliases.alias IS 'An alternative name, e.g., "Ground Chuck" for the master item "Ground Beef".';

-- DONE
-- 11. Create tables for user shopping lists.
CREATE TABLE IF NOT EXISTS public.shopping_lists (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    user_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE,
    name TEXT NOT NULL,
    created_at TIMESTAMPTZ DEFAULT now() NOT NULL
);
COMMENT ON TABLE public.shopping_lists IS 'Stores user-created shopping lists, e.g., "Weekly Groceries".';

-- DONE
CREATE TABLE IF NOT EXISTS public.shopping_list_items (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    shopping_list_id BIGINT NOT NULL REFERENCES public.shopping_lists(id) ON DELETE CASCADE,
    master_item_id BIGINT REFERENCES public.master_grocery_items(id),
    custom_item_name TEXT,
    quantity NUMERIC DEFAULT 1 NOT NULL,
    is_purchased BOOLEAN DEFAULT false NOT NULL,
    added_at TIMESTAMPTZ DEFAULT now() NOT NULL,
    -- Ensure one of the item identifiers is present
    CONSTRAINT must_have_item_identifier CHECK (master_item_id IS NOT NULL OR custom_item_name IS NOT NULL)
);
COMMENT ON TABLE public.shopping_list_items IS 'Contains individual items for a specific shopping list.';
COMMENT ON COLUMN public.shopping_list_items.custom_item_name IS 'For items not in the master list, e.g., "Grandma''s special spice mix".';
COMMENT ON COLUMN public.shopping_list_items.is_purchased IS 'Lets users check items off their list as they shop.';


-- DONE
-- 12. Create a table to store user-submitted corrections for flyer items.
CREATE TABLE IF NOT EXISTS public.suggested_corrections (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    flyer_item_id BIGINT NOT NULL REFERENCES public.flyer_items(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES public.users(id),
    correction_type TEXT NOT NULL,
    suggested_value TEXT NOT NULL,
    status TEXT DEFAULT 'pending' NOT NULL,
    created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
    reviewed_notes TEXT,
    reviewed_at TIMESTAMPTZ
);
COMMENT ON TABLE public.suggested_corrections IS 'A queue for user-submitted data corrections, enabling crowdsourced data quality improvements.';
COMMENT ON COLUMN public.suggested_corrections.correction_type IS 'The type of error the user is reporting.';
COMMENT ON COLUMN public.suggested_corrections.suggested_value IS 'The corrected value proposed by the user (e.g., a new price or master_item_id).';
COMMENT ON COLUMN public.suggested_corrections.status IS 'The moderation status of the correction: pending, approved, or rejected.';

-- DONE
-- 13. Create a table for prices submitted directly by users from in-store.
CREATE TABLE IF NOT EXISTS public.user_submitted_prices (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    user_id UUID NOT NULL REFERENCES public.users(id),
    master_item_id BIGINT NOT NULL REFERENCES public.master_grocery_items(id),
    store_id BIGINT NOT NULL REFERENCES public.stores(id),
    price_in_cents INTEGER NOT NULL,
    photo_url TEXT,
    upvotes INTEGER DEFAULT 0 NOT NULL,
    downvotes INTEGER DEFAULT 0 NOT NULL,
    created_at TIMESTAMPTZ DEFAULT now() NOT NULL
);
COMMENT ON TABLE public.user_submitted_prices IS 'Stores item prices submitted by users directly from physical stores.';
COMMENT ON COLUMN public.user_submitted_prices.photo_url IS 'URL to user-submitted photo evidence of the price.';
COMMENT ON COLUMN public.user_submitted_prices.upvotes IS 'Community validation score indicating accuracy.';

-- 14. Pre-populate categories table from a predefined list.
INSERT INTO public.categories (name) VALUES
('Fruits & Vegetables'), ('Meat & Seafood'), ('Dairy & Eggs'), ('Bakery & Bread'), 
('Pantry & Dry Goods'), ('Beverages'), ('Frozen Foods'), ('Snacks'), ('Household & Cleaning'),
('Personal Care & Health'), ('Baby & Child'), ('Pet Supplies'), ('Deli & Prepared Foods'),
('Canned Goods'), ('Condiments & Spices'), ('Breakfast & Cereal'), ('Organic'), 
('International Foods'), ('Other/Miscellaneous')
ON CONFLICT (name) DO NOTHING;

-- DONE
-- A table to store brand information.
CREATE TABLE IF NOT EXISTS public.brands (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    name TEXT NOT NULL UNIQUE
);
COMMENT ON TABLE public.brands IS 'Stores brand names like "Coca-Cola", "Maple Leaf", or "Kraft".';

-- DONE
-- A table for specific products, linking a master item with a brand and size.
CREATE TABLE IF NOT EXISTS public.products (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    master_item_id BIGINT NOT NULL REFERENCES public.master_grocery_items(id),
    brand_id BIGINT REFERENCES public.brands(id),
    name TEXT NOT NULL,
    description TEXT,
    size TEXT,
    upc_code TEXT UNIQUE
);
COMMENT ON TABLE public.products IS 'Represents a specific, sellable product, combining a generic item with a brand and size.';
COMMENT ON COLUMN public.products.upc_code IS 'Universal Product Code, if available, for exact product matching.';
COMMENT ON COLUMN public.products.brand_id IS 'Can be null for generic/store-brand items.';
COMMENT ON COLUMN public.products.name IS 'Prime Raised without Antibiotics Chicken Breast.';
COMMENT ON COLUMN public.products.size IS 'e.g., "4L", "500g".';


-- Then, you would update 'flyer_items' to link to this new table.
ALTER TABLE public.flyer_items
ADD COLUMN IF NOT EXISTS product_id BIGINT REFERENCES public.products(id);
ADD CONSTRAINT flyer_items_product_id_fkey
FOREIGN KEY (product_id) REFERENCES public.products(id);

-- Enable trigram support for fuzzy string matching
CREATE EXTENSION IF NOT EXISTS pg_trgm;
-- Add a GIN index to the 'item' column for fast fuzzy text searching.
-- This requires the pg_trgm extension.
CREATE INDEX IF NOT EXISTS flyer_items_item_trgm_idx ON public.flyer_items USING GIN (item gin_trgm_ops);

-- First, enable the PostGIS extension if you haven't already.
-- In Supabase, you can do this under Database -> Extensions.
-- CREATE EXTENSION IF NOT EXISTS postgis;

-- DONE
-- A table to store individual store locations with geographic data.
CREATE TABLE IF NOT EXISTS public.store_locations (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    store_id BIGINT NOT NULL REFERENCES public.stores(id) ON DELETE CASCADE,
    address TEXT NOT NULL,
    city TEXT,
    province_state TEXT,
    postal_code TEXT,
    -- Use the 'geography' type for lat/lon data.
    location GEOGRAPHY(Point, 4326)
);
CREATE INDEX IF NOT EXISTS store_locations_geo_idx ON public.store_locations USING GIST (location);
COMMENT ON TABLE public.store_locations IS 'Stores physical locations of stores with geographic data for proximity searches.';
COMMENT ON COLUMN public.store_locations.location IS 'Geographic coordinates (longitude, latitude) of the store.';
-- Add a GIST index for efficient geographic queries.
-- This requires the postgis extension.
CREATE INDEX IF NOT EXISTS store_locations_geo_idx ON public.store_locations USING GIST (location);

-- DONE
-- You might also need a linking table if one flyer is valid for multiple locations.
CREATE TABLE IF NOT EXISTS public.flyer_locations (
    flyer_id BIGINT NOT NULL REFERENCES public.flyers(id) ON DELETE CASCADE,
    store_location_id BIGINT NOT NULL REFERENCES public.store_locations(id) ON DELETE CASCADE,
    PRIMARY KEY (flyer_id, store_location_id)
);
COMMENT ON TABLE public.flyer_locations IS 'A linking table associating a single flyer with multiple store locations where its deals are valid.';

-- done
-- A table to store recipes, which can be user-created or pre-populated.
CREATE TABLE IF NOT EXISTS public.recipes (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    user_id UUID REFERENCES public.users(id) ON DELETE SET NULL, -- Can be a system recipe (user_id is NULL) or user-submitted
    name TEXT NOT NULL,
    description TEXT,
    instructions TEXT,
    prep_time_minutes INTEGER,
    cook_time_minutes INTEGER,
    servings INTEGER,
    photo_url TEXT,
    -- Optional nutritional information
    calories_per_serving INTEGER,
    protein_grams NUMERIC,
    fat_grams NUMERIC,
    carb_grams NUMERIC,
    -- Aggregated rating data for fast sorting/display
    avg_rating NUMERIC(2,1) DEFAULT 0.0 NOT NULL,
    rating_count INTEGER DEFAULT 0 NOT NULL,
    created_at TIMESTAMPTZ DEFAULT now() NOT NULL
);
COMMENT ON TABLE public.recipes IS 'Stores recipes that can be used to generate shopping lists.';
COMMENT ON COLUMN public.recipes.servings IS 'The number of servings this recipe yields.';

-- done
-- A linking table for ingredients required for each recipe.
CREATE TABLE IF NOT EXISTS public.recipe_ingredients (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    recipe_id BIGINT NOT NULL REFERENCES public.recipes(id) ON DELETE CASCADE,
    master_item_id BIGINT NOT NULL REFERENCES public.master_grocery_items(id),
    quantity NUMERIC NOT NULL,
    unit TEXT NOT NULL
);
COMMENT ON TABLE public.recipe_ingredients IS 'Defines the ingredients and quantities needed for a recipe.';
COMMENT ON COLUMN public.recipe_ingredients.unit IS 'e.g., "cups", "tbsp", "g", "each".';

-- done
-- A table to store a predefined list of tags for recipes.
CREATE TABLE IF NOT EXISTS public.tags (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    name TEXT NOT NULL UNIQUE
);
COMMENT ON TABLE public.tags IS 'Stores tags for categorizing recipes, e.g., "Vegetarian", "Quick & Easy".';

-- done
-- A linking table to associate multiple tags with a recipe.
CREATE TABLE IF NOT EXISTS public.recipe_tags (
    recipe_id BIGINT NOT NULL REFERENCES public.recipes(id) ON DELETE CASCADE,
    tag_id BIGINT NOT NULL REFERENCES public.tags(id) ON DELETE CASCADE,
    PRIMARY KEY (recipe_id, tag_id)
);
COMMENT ON TABLE public.recipe_tags IS 'A linking table to associate multiple tags with a single recipe.';

-- done
-- A table to store individual user ratings for recipes.
CREATE TABLE IF NOT EXISTS public.recipe_ratings (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    recipe_id BIGINT NOT NULL REFERENCES public.recipes(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE,
    rating INTEGER NOT NULL CHECK (rating >= 1 AND rating <= 5),
    comment TEXT,
    created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
    UNIQUE(recipe_id, user_id) -- A user can only rate a recipe once.
);
COMMENT ON TABLE public.recipe_ratings IS 'Stores individual user ratings for recipes, ensuring a user can only rate a recipe once.';


-- DONE
-- A table to store a user's collection of planned meals for a date range.
CREATE TABLE IF NOT EXISTS public.menu_plans (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    user_id UUID NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE,
    name TEXT NOT NULL,
    start_date DATE NOT NULL,
    end_date DATE NOT NULL,
    created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
    CONSTRAINT date_range_check CHECK (end_date >= start_date)
);
COMMENT ON TABLE public.menu_plans IS 'Represents a user''s meal plan for a specific period, e.g., "Week of Oct 23".';

-- DONE
-- A table to associate a recipe with a specific date and meal type within a menu plan.
CREATE TABLE IF NOT EXISTS public.planned_meals (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    menu_plan_id BIGINT NOT NULL REFERENCES public.menu_plans(id) ON DELETE CASCADE,
    recipe_id BIGINT NOT NULL REFERENCES public.recipes(id) ON DELETE CASCADE,
    plan_date DATE NOT NULL,
    meal_type TEXT NOT NULL,
    servings_to_cook INTEGER
);
COMMENT ON TABLE public.planned_meals IS 'Assigns a recipe to a specific day and meal type within a user''s menu plan.';
COMMENT ON COLUMN public.planned_meals.meal_type IS 'The designated meal for the recipe, e.g., ''Breakfast'', ''Lunch'', ''Dinner''.';

-- DONE
-- A table to track the grocery items a user currently has in their pantry.
CREATE TABLE IF NOT EXISTS public.pantry_items (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    user_id UUID NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE,
    master_item_id BIGINT NOT NULL REFERENCES public.master_grocery_items(id) ON DELETE CASCADE,
    quantity NUMERIC NOT NULL,
    unit TEXT,
    best_before_date DATE,
    updated_at TIMESTAMPTZ DEFAULT now() NOT NULL,
    UNIQUE(user_id, master_item_id)
);
COMMENT ON TABLE public.pantry_items IS 'Tracks a user''s personal inventory of grocery items to enable smart shopping lists.';
COMMENT ON COLUMN public.pantry_items.quantity IS 'The current amount of the item. Convention: use grams for weight, mL for volume where applicable.';
COMMENT ON COLUMN public.pantry_items.unit IS 'e.g., ''g'', ''ml'', ''items''. Should align with recipe_ingredients.unit and quantity convention.';



CREATE INDEX IF NOT EXISTS idx_password_reset_tokens_token_hash ON public.password_reset_tokens(token_hash);
-- A table to store password reset tokens.
CREATE TABLE IF NOT EXISTS public.password_reset_tokens (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    user_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE,
    token_hash TEXT NOT NULL UNIQUE,
    expires_at TIMESTAMPTZ NOT NULL,
    created_at TIMESTAMPTZ DEFAULT now() NOT NULL
);
COMMENT ON TABLE public.password_reset_tokens IS 'Stores secure, single-use tokens for password reset requests.';
COMMENT ON COLUMN public.password_reset_tokens.token_hash IS 'A bcrypt hash of the reset token sent to the user.';
COMMENT ON COLUMN public.password_reset_tokens.expires_at IS 'The timestamp when this token is no longer valid.';

-- A table to store unit conversion factors for specific master grocery items.
CREATE TABLE IF NOT EXISTS public.unit_conversions (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    master_item_id BIGINT NOT NULL REFERENCES public.master_grocery_items(id) ON DELETE CASCADE,
    from_unit TEXT NOT NULL,
    to_unit TEXT NOT NULL,
    factor NUMERIC NOT NULL,
    UNIQUE(master_item_id, from_unit, to_unit)
);
COMMENT ON TABLE public.unit_conversions IS 'Stores item-specific unit conversion factors (e.g., grams of flour to cups).';
COMMENT ON COLUMN public.unit_conversions.factor IS 'The multiplication factor to convert from_unit to to_unit.';
