"""
Netrun RBAC RLS Generator - Generate PostgreSQL Row-Level Security policies.

Following Netrun Systems SDLC v2.3 standards.
"""

from typing import List, Optional


def generate_rls_policy(
    table_name: str,
    *,
    tenant_variable: str = "app.current_tenant_id",
    user_variable: str = "app.current_user_id",
    policy_name: Optional[str] = None,
    include_user_check: bool = False,
    share_level_column: Optional[str] = "share_level",
) -> str:
    """
    Generate RLS policy SQL for a single table.

    Args:
        table_name: Name of the table
        tenant_variable: PostgreSQL session variable for tenant ID
        user_variable: PostgreSQL session variable for user ID
        policy_name: Custom policy name (defaults to {table}_tenant_isolation)
        include_user_check: Include user-level visibility check
        share_level_column: Column name for share level (None to skip)

    Returns:
        SQL string for creating the RLS policy
    """
    policy_name = policy_name or f"{table_name}_tenant_isolation"

    # Build the USING clause
    if include_user_check and share_level_column:
        using_clause = f"""
        tenant_id = current_setting('{tenant_variable}')::uuid
        AND (
            -- Owner can always see their own records
            created_by = current_setting('{user_variable}')::uuid
            -- Or record is shared at team/tenant level
            OR {share_level_column} IN ('team', 'tenant')
        )
        """.strip()
    else:
        using_clause = f"tenant_id = current_setting('{tenant_variable}')::uuid"

    return f"""
-- ============================================
-- RLS Policy for: {table_name}
-- ============================================

-- Enable Row-Level Security
ALTER TABLE {table_name} ENABLE ROW LEVEL SECURITY;

-- Force RLS for table owner (security best practice)
ALTER TABLE {table_name} FORCE ROW LEVEL SECURITY;

-- Create tenant isolation policy
DROP POLICY IF EXISTS {policy_name} ON {table_name};
CREATE POLICY {policy_name} ON {table_name}
    FOR ALL
    USING ({using_clause});

-- Optional: Create policy for INSERT (auto-set tenant_id)
-- This ensures new rows get the correct tenant_id
DROP POLICY IF EXISTS {policy_name}_insert ON {table_name};
CREATE POLICY {policy_name}_insert ON {table_name}
    FOR INSERT
    WITH CHECK (tenant_id = current_setting('{tenant_variable}')::uuid);
"""


def generate_rls_policies(
    table_names: List[str],
    *,
    tenant_variable: str = "app.current_tenant_id",
    user_variable: str = "app.current_user_id",
    include_user_check: bool = False,
) -> str:
    """
    Generate RLS policies for multiple tables.

    Args:
        table_names: List of table names
        tenant_variable: PostgreSQL session variable for tenant ID
        user_variable: PostgreSQL session variable for user ID
        include_user_check: Include user-level visibility check

    Returns:
        SQL string for all policies
    """
    header = f"""
-- ============================================
-- Netrun RBAC - Row-Level Security Policies
-- Generated by netrun-rbac v3.0.0
-- ============================================

-- This script enables RLS on the specified tables and creates
-- tenant isolation policies. Run this after creating the tables.

-- Session variables used:
--   {tenant_variable}: Current tenant UUID
--   {user_variable}: Current user UUID (optional)

-- IMPORTANT: Set these variables at the start of each session/transaction:
--   SET LOCAL {tenant_variable} = 'your-tenant-uuid';
--   SET LOCAL {user_variable} = 'your-user-uuid';

"""

    policies = [header]

    for table in table_names:
        policies.append(generate_rls_policy(
            table,
            tenant_variable=tenant_variable,
            user_variable=user_variable,
            include_user_check=include_user_check,
        ))

    return "\n".join(policies)


def enable_rls_on_table(table_name: str) -> str:
    """
    Generate SQL to enable RLS on a table.

    Args:
        table_name: Name of the table

    Returns:
        SQL string
    """
    return f"""
ALTER TABLE {table_name} ENABLE ROW LEVEL SECURITY;
ALTER TABLE {table_name} FORCE ROW LEVEL SECURITY;
"""


def disable_rls_on_table(table_name: str) -> str:
    """
    Generate SQL to disable RLS on a table.

    Args:
        table_name: Name of the table

    Returns:
        SQL string
    """
    return f"""
ALTER TABLE {table_name} DISABLE ROW LEVEL SECURITY;
"""


def generate_full_tenancy_setup(
    app_tables: List[str],
    *,
    tenant_variable: str = "app.current_tenant_id",
    user_variable: str = "app.current_user_id",
    include_core_tables: bool = True,
) -> str:
    """
    Generate complete SQL for tenancy setup including core tables and RLS.

    Args:
        app_tables: List of application table names that need tenancy
        tenant_variable: PostgreSQL session variable for tenant ID
        user_variable: PostgreSQL session variable for user ID
        include_core_tables: Include creation of tenants, teams, memberships tables

    Returns:
        Complete SQL setup script
    """
    sections = []

    # Header
    sections.append("""
-- ============================================
-- Netrun RBAC - Full Tenancy Setup
-- Generated by netrun-rbac v3.0.0
-- ============================================

-- This script sets up multi-tenant infrastructure including:
-- 1. Core tables (tenants, teams, memberships, resource_shares)
-- 2. Row-Level Security policies
-- 3. Required indexes

BEGIN;
""")

    if include_core_tables:
        sections.append("""
-- ============================================
-- Section 1: Core Tables
-- ============================================

-- Tenants table
CREATE TABLE IF NOT EXISTS tenants (
    id UUID PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    slug VARCHAR(100) UNIQUE NOT NULL,
    domain VARCHAR(255) UNIQUE,
    subscription_tier VARCHAR(50) NOT NULL DEFAULT 'basic',
    status VARCHAR(20) NOT NULL DEFAULT 'trial',
    max_users INTEGER NOT NULL DEFAULT 10,
    max_teams INTEGER NOT NULL DEFAULT 20,
    max_storage_gb INTEGER NOT NULL DEFAULT 10,
    settings JSONB NOT NULL DEFAULT '{}',
    security_settings JSONB NOT NULL DEFAULT '{}',
    enabled_features JSONB NOT NULL DEFAULT '[]',
    billing_email VARCHAR(255),
    technical_contact VARCHAR(255),
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ,
    is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
    deleted_at TIMESTAMPTZ
);

CREATE INDEX IF NOT EXISTS idx_tenants_slug ON tenants(slug);
CREATE INDEX IF NOT EXISTS idx_tenants_domain ON tenants(domain);
CREATE INDEX IF NOT EXISTS idx_tenants_status ON tenants(status);

-- Teams table (hierarchical)
CREATE TABLE IF NOT EXISTS teams (
    id UUID PRIMARY KEY,
    tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
    name VARCHAR(200) NOT NULL,
    description TEXT,
    parent_team_id UUID REFERENCES teams(id) ON DELETE CASCADE,
    path VARCHAR(1000) NOT NULL DEFAULT '/',
    depth INTEGER NOT NULL DEFAULT 0,
    settings JSONB NOT NULL DEFAULT '{}',
    is_public BOOLEAN NOT NULL DEFAULT FALSE,
    max_members INTEGER NOT NULL DEFAULT 100,
    created_by UUID,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ,
    updated_by UUID,
    is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
    deleted_at TIMESTAMPTZ,
    deleted_by UUID
);

CREATE INDEX IF NOT EXISTS idx_teams_tenant_id ON teams(tenant_id);
CREATE INDEX IF NOT EXISTS idx_teams_parent_team_id ON teams(parent_team_id);
CREATE INDEX IF NOT EXISTS idx_teams_path ON teams(path text_pattern_ops);
CREATE INDEX IF NOT EXISTS idx_teams_tenant_parent ON teams(tenant_id, parent_team_id);

-- Tenant memberships
CREATE TABLE IF NOT EXISTS tenant_memberships (
    id UUID PRIMARY KEY,
    tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
    user_id UUID NOT NULL,
    role VARCHAR(20) NOT NULL DEFAULT 'member',
    is_active BOOLEAN NOT NULL DEFAULT TRUE,
    custom_permissions JSONB NOT NULL DEFAULT '[]',
    invited_by UUID,
    invited_at TIMESTAMPTZ,
    joined_at TIMESTAMPTZ,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ,
    UNIQUE(tenant_id, user_id)
);

CREATE INDEX IF NOT EXISTS idx_tenant_membership_tenant ON tenant_memberships(tenant_id);
CREATE INDEX IF NOT EXISTS idx_tenant_membership_user ON tenant_memberships(user_id);
CREATE INDEX IF NOT EXISTS idx_tenant_membership_user_active ON tenant_memberships(user_id, is_active);

-- Team memberships
CREATE TABLE IF NOT EXISTS team_memberships (
    id UUID PRIMARY KEY,
    team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE,
    user_id UUID NOT NULL,
    role VARCHAR(20) NOT NULL DEFAULT 'member',
    inherited_from UUID REFERENCES teams(id) ON DELETE CASCADE,
    is_active BOOLEAN NOT NULL DEFAULT TRUE,
    custom_permissions JSONB NOT NULL DEFAULT '[]',
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ,
    UNIQUE(team_id, user_id)
);

CREATE INDEX IF NOT EXISTS idx_team_membership_team ON team_memberships(team_id);
CREATE INDEX IF NOT EXISTS idx_team_membership_user ON team_memberships(user_id);
CREATE INDEX IF NOT EXISTS idx_team_membership_inherited ON team_memberships(inherited_from);

-- Resource shares
CREATE TABLE IF NOT EXISTS resource_shares (
    id UUID PRIMARY KEY,
    tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
    resource_type VARCHAR(100) NOT NULL,
    resource_id UUID NOT NULL,
    shared_with_user_id UUID,
    shared_with_team_id UUID REFERENCES teams(id) ON DELETE CASCADE,
    shared_with_tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,
    shared_externally VARCHAR(255),
    permission VARCHAR(20) NOT NULL DEFAULT 'view',
    expires_at TIMESTAMPTZ,
    shared_by UUID NOT NULL,
    message VARCHAR(500),
    access_count INTEGER NOT NULL DEFAULT 0,
    last_accessed_at TIMESTAMPTZ,
    external_token VARCHAR(100) UNIQUE,
    metadata JSONB NOT NULL DEFAULT '{}',
    is_revoked BOOLEAN NOT NULL DEFAULT FALSE,
    revoked_at TIMESTAMPTZ,
    revoked_by UUID,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ,
    created_by UUID,
    updated_by UUID,
    CONSTRAINT chk_resource_share_one_target CHECK (
        (shared_with_user_id IS NOT NULL)::int +
        (shared_with_team_id IS NOT NULL)::int +
        (shared_with_tenant_id IS NOT NULL)::int +
        (shared_externally IS NOT NULL)::int = 1
    )
);

CREATE INDEX IF NOT EXISTS idx_resource_share_tenant ON resource_shares(tenant_id);
CREATE INDEX IF NOT EXISTS idx_resource_share_resource ON resource_shares(resource_type, resource_id);
CREATE INDEX IF NOT EXISTS idx_resource_share_user ON resource_shares(shared_with_user_id) WHERE shared_with_user_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_resource_share_team ON resource_shares(shared_with_team_id) WHERE shared_with_team_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_resource_share_token ON resource_shares(external_token) WHERE external_token IS NOT NULL;
""")

    # RLS for core tables
    core_tables_with_rls = ["teams"]  # tenants doesn't have tenant_id

    sections.append(f"""
-- ============================================
-- Section 2: RLS for Core Tables
-- ============================================
""")

    for table in core_tables_with_rls:
        sections.append(generate_rls_policy(
            table,
            tenant_variable=tenant_variable,
            user_variable=user_variable,
        ))

    # RLS for application tables
    if app_tables:
        sections.append(f"""
-- ============================================
-- Section 3: RLS for Application Tables
-- ============================================
""")

        for table in app_tables:
            sections.append(generate_rls_policy(
                table,
                tenant_variable=tenant_variable,
                user_variable=user_variable,
            ))

    # Footer
    sections.append("""
-- ============================================
-- Setup Complete
-- ============================================

COMMIT;

-- IMPORTANT: After running this script, ensure your application:
-- 1. Sets session variables before each transaction:
--    SET LOCAL app.current_tenant_id = 'tenant-uuid';
--    SET LOCAL app.current_user_id = 'user-uuid';
--
-- 2. Uses the netrun-rbac middleware for automatic setup:
--    from netrun.rbac.middleware import setup_tenancy_middleware
--    setup_tenancy_middleware(app, config, get_session=get_db)
""")

    return "\n".join(sections)
