-- Portfolio Intelligence Platform Database Schema
-- For Supabase PostgreSQL with Auth Integration

-- Enable UUID extension (if not already enabled)
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

-- Helper function to convert strings to UUIDs (for demo portfolio IDs)
CREATE OR REPLACE FUNCTION string_to_portfolio_uuid(input_string TEXT)
RETURNS UUID
LANGUAGE plpgsql
IMMUTABLE
SECURITY INVOKER
SET search_path = ''
AS $$
BEGIN
    -- Use URL namespace UUID constant (6ba7b811-9dad-11d1-80b4-00c04fd430c8)
    -- uuid_ns_url() is a constant in PostgreSQL, not a function
    RETURN public.uuid_generate_v5('6ba7b811-9dad-11d1-80b4-00c04fd430c8'::uuid, input_string);
END;
$$;

-- Grant execute to authenticated and anon roles
GRANT EXECUTE ON FUNCTION string_to_portfolio_uuid TO authenticated, anon;

-- Users table (linked to Supabase Auth - CRITICAL FIX)
CREATE TABLE IF NOT EXISTS users (
    id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
    email VARCHAR(255) UNIQUE NOT NULL,
    username VARCHAR(100),
    is_active BOOLEAN DEFAULT true,
    is_demo BOOLEAN DEFAULT false,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_users_id ON users(id);
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);

-- Portfolios table
CREATE TABLE IF NOT EXISTS portfolios (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    name VARCHAR(200) NOT NULL,
    description TEXT,
    risk_tolerance VARCHAR(20) DEFAULT 'moderate',
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_portfolios_user_id ON portfolios(user_id);
CREATE INDEX IF NOT EXISTS idx_portfolios_created_at ON portfolios(created_at DESC);

-- Portfolio inputs table (for auto-save history - keeps last 3 per user)
CREATE TABLE IF NOT EXISTS portfolio_inputs (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    description TEXT NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_portfolio_inputs_user_created ON portfolio_inputs(user_id, created_at DESC);

-- Trigger function to limit portfolio inputs to 3 per user
CREATE OR REPLACE FUNCTION limit_portfolio_inputs_per_user()
RETURNS TRIGGER
LANGUAGE plpgsql
SECURITY INVOKER
SET search_path = ''
AS $$
BEGIN
    -- Keep only the 3 most recent entries per user
    DELETE FROM public.portfolio_inputs
    WHERE id IN (
        SELECT id
        FROM (
            SELECT id,
                   ROW_NUMBER() OVER (
                       PARTITION BY user_id
                       ORDER BY created_at DESC
                   ) AS row_num
            FROM public.portfolio_inputs
            WHERE user_id = NEW.user_id
        ) ranked
        WHERE row_num > 3
    );

    RETURN NULL;
END;
$$;

-- Create trigger for automatic cleanup
DROP TRIGGER IF EXISTS trigger_limit_portfolio_inputs ON portfolio_inputs;
CREATE TRIGGER trigger_limit_portfolio_inputs
    AFTER INSERT ON portfolio_inputs
    FOR EACH ROW EXECUTE FUNCTION limit_portfolio_inputs_per_user();

-- Portfolio holdings table
CREATE TABLE IF NOT EXISTS portfolio_holdings (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    portfolio_id UUID NOT NULL REFERENCES portfolios(id) ON DELETE CASCADE,
    ticker VARCHAR(20) NOT NULL,
    quantity NUMERIC(20, 8) NOT NULL,
    cost_basis NUMERIC(20, 2),
    asset_type VARCHAR(20) DEFAULT 'stock',
    added_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_holdings_portfolio_id ON portfolio_holdings(portfolio_id);
CREATE INDEX IF NOT EXISTS idx_holdings_ticker ON portfolio_holdings(ticker);

-- Portfolio analyses table
CREATE TABLE IF NOT EXISTS portfolio_analyses (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    portfolio_id UUID NOT NULL REFERENCES portfolios(id) ON DELETE CASCADE,
    analysis_date TIMESTAMP WITH TIME ZONE DEFAULT NOW(),

    -- Analysis results (JSONB for flexibility)
    holdings_snapshot JSONB NOT NULL,
    market_data JSONB,
    risk_metrics JSONB,
    optimisation_results JSONB,
    ai_synthesis TEXT,
    recommendations JSONB,

    -- Metadata
    reasoning_steps JSONB,
    mcp_calls JSONB,
    sentiment_data JSONB,
    execution_time_ms INTEGER,
    model_version VARCHAR(50),

    -- Computed fields
    total_value NUMERIC(20, 2),
    health_score INTEGER CHECK (health_score >= 0 AND health_score <= 10),
    risk_level VARCHAR(20),

    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_analyses_portfolio_id ON portfolio_analyses(portfolio_id);
CREATE INDEX IF NOT EXISTS idx_analyses_date ON portfolio_analyses(analysis_date DESC);
CREATE INDEX IF NOT EXISTS idx_analyses_health_score ON portfolio_analyses(health_score);
CREATE INDEX IF NOT EXISTS idx_analyses_sentiment_data ON portfolio_analyses USING GIN (sentiment_data);

-- Function to automatically create user profile on signup (CRITICAL FOR AUTH)
-- Uses SECURITY DEFINER with empty search_path to bypass RLS on INSERT
-- Fully qualifies table name (public.users) to work with empty search_path
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS TRIGGER
LANGUAGE plpgsql
SECURITY DEFINER SET search_path = ''
AS $$
BEGIN
    INSERT INTO public.users (id, email, username, is_active, is_demo)
    VALUES (
        NEW.id,
        NEW.email,
        COALESCE(
            NEW.raw_user_meta_data ->> 'username',
            split_part(NEW.email, '@', 1)
        ),
        true,
        false
    );
    RETURN NEW;
EXCEPTION
    WHEN OTHERS THEN
        -- Log error but don't block signup
        RAISE WARNING 'Failed to create user profile: %', SQLERRM;
        RETURN NEW;
END;
$$;

-- Create trigger for automatic profile creation
DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users;
CREATE TRIGGER on_auth_user_created
    AFTER INSERT ON auth.users
    FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();

-- Function to update updated_at timestamp
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER
LANGUAGE plpgsql
SECURITY INVOKER
SET search_path = ''
AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$;

-- Triggers for updated_at
DROP TRIGGER IF EXISTS update_users_updated_at ON users;
CREATE TRIGGER update_users_updated_at BEFORE UPDATE ON users
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

DROP TRIGGER IF EXISTS update_portfolios_updated_at ON portfolios;
CREATE TRIGGER update_portfolios_updated_at BEFORE UPDATE ON portfolios
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

-- Grant permissions (Supabase uses RLS - Row Level Security)
-- These are basic permissions; adjust based on your auth setup
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
ALTER TABLE portfolios ENABLE ROW LEVEL SECURITY;
ALTER TABLE portfolio_inputs ENABLE ROW LEVEL SECURITY;
ALTER TABLE portfolio_holdings ENABLE ROW LEVEL SECURITY;
ALTER TABLE portfolio_analyses ENABLE ROW LEVEL SECURITY;

-- RLS policies with performance optimizations (wrapped auth.uid() = 20x faster)
-- Users table policies
DROP POLICY IF EXISTS users_read_own ON users;
CREATE POLICY users_read_own ON users
    FOR SELECT
    TO authenticated
    USING ((SELECT auth.uid()) = id);

DROP POLICY IF EXISTS users_update_own ON users;
CREATE POLICY users_update_own ON users
    FOR UPDATE
    TO authenticated
    USING ((SELECT auth.uid()) = id)
    WITH CHECK ((SELECT auth.uid()) = id);

DROP POLICY IF EXISTS users_insert_own ON users;
CREATE POLICY users_insert_own ON users
    FOR INSERT
    TO authenticated
    WITH CHECK ((SELECT auth.uid()) = id);

-- Portfolios table policies
DROP POLICY IF EXISTS portfolios_read_own ON portfolios;
CREATE POLICY portfolios_read_own ON portfolios
    FOR SELECT
    TO authenticated
    USING ((SELECT auth.uid()) = user_id);

DROP POLICY IF EXISTS portfolios_insert_own ON portfolios;
CREATE POLICY portfolios_insert_own ON portfolios
    FOR INSERT
    TO authenticated
    WITH CHECK ((SELECT auth.uid()) = user_id);

DROP POLICY IF EXISTS portfolios_update_own ON portfolios;
CREATE POLICY portfolios_update_own ON portfolios
    FOR UPDATE
    TO authenticated
    USING ((SELECT auth.uid()) = user_id)
    WITH CHECK ((SELECT auth.uid()) = user_id);

DROP POLICY IF EXISTS portfolios_delete_own ON portfolios;
CREATE POLICY portfolios_delete_own ON portfolios
    FOR DELETE
    TO authenticated
    USING ((SELECT auth.uid()) = user_id);

-- Portfolio inputs table policies
DROP POLICY IF EXISTS portfolio_inputs_read_own ON portfolio_inputs;
CREATE POLICY portfolio_inputs_read_own ON portfolio_inputs
    FOR SELECT
    TO authenticated
    USING ((SELECT auth.uid()) = user_id);

DROP POLICY IF EXISTS portfolio_inputs_insert_own ON portfolio_inputs;
CREATE POLICY portfolio_inputs_insert_own ON portfolio_inputs
    FOR INSERT
    TO authenticated
    WITH CHECK ((SELECT auth.uid()) = user_id);

DROP POLICY IF EXISTS portfolio_inputs_delete_own ON portfolio_inputs;
CREATE POLICY portfolio_inputs_delete_own ON portfolio_inputs
    FOR DELETE
    TO authenticated
    USING ((SELECT auth.uid()) = user_id);

-- Holdings policies (optimized subquery pattern)
DROP POLICY IF EXISTS holdings_access_own ON portfolio_holdings;
CREATE POLICY holdings_access_own ON portfolio_holdings
    FOR ALL
    TO authenticated
    USING (
        portfolio_id IN (
            SELECT id FROM portfolios
            WHERE user_id = (SELECT auth.uid())
        )
    )
    WITH CHECK (
        portfolio_id IN (
            SELECT id FROM portfolios
            WHERE user_id = (SELECT auth.uid())
        )
    );

-- Analyses policies
DROP POLICY IF EXISTS analyses_access_own ON portfolio_analyses;
CREATE POLICY analyses_access_own ON portfolio_analyses
    FOR ALL
    TO authenticated
    USING (
        portfolio_id IN (
            SELECT id FROM portfolios
            WHERE user_id = (SELECT auth.uid())
        )
    )
    WITH CHECK (
        portfolio_id IN (
            SELECT id FROM portfolios
            WHERE user_id = (SELECT auth.uid())
        )
    );

-- Grant permissions
GRANT USAGE ON SCHEMA public TO anon, authenticated;
GRANT ALL ON ALL TABLES IN SCHEMA public TO anon, authenticated;
GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO anon, authenticated;
GRANT ALL ON ALL FUNCTIONS IN SCHEMA public TO anon, authenticated;
