-- ============================================================================
-- 数据完整性强约束 - 确保财务与核心数据100%正确
-- ============================================================================
-- 目标：系统错误率 ≤ 0.01% (万分之一)
-- 范围：production_plans, inventory_records, payments, accounts_payable
-- ============================================================================

-- ============================================================================
-- 1. 创建数据校验函数
-- ============================================================================

-- 1.1 验证计划数量一致性（completed_quantity = SUM(inventory_records.quantity)）
CREATE OR REPLACE FUNCTION validate_plan_quantity_consistency()
RETURNS TRIGGER LANGUAGE plpgsql AS $$
DECLARE
  v_inventory_total NUMERIC;
  v_tolerance NUMERIC := 0.01; -- 允许0.01米的浮点误差
BEGIN
  -- 仅当 completed_quantity 被更新时触发
  IF OLD.completed_quantity IS DISTINCT FROM NEW.completed_quantity THEN
    SELECT COALESCE(SUM(quantity), 0) INTO v_inventory_total
    FROM inventory_records
    WHERE plan_id = NEW.id;
    
    IF ABS(NEW.completed_quantity - v_inventory_total) > v_tolerance THEN
      RAISE EXCEPTION 
        '数据完整性错误：计划 % 的完成数量(%)与入库记录总和(%)不一致，差异: %',
        NEW.plan_code, NEW.completed_quantity, v_inventory_total, 
        NEW.completed_quantity - v_inventory_total;
    END IF;
  END IF;
  
  RETURN NEW;
END;
$$;

-- 1.2 验证入库记录不能为负数
CREATE OR REPLACE FUNCTION validate_inventory_record_positive()
RETURNS TRIGGER LANGUAGE plpgsql AS $$
BEGIN
  IF NEW.quantity < 0 THEN
    RAISE EXCEPTION '入库数量不能为负数: %', NEW.quantity;
  END IF;
  
  IF NEW.rolls < 0 THEN
    RAISE EXCEPTION '入库匹数不能为负数: %', NEW.rolls;
  END IF;
  
  IF NEW.price_per_meter < 0 THEN
    RAISE EXCEPTION '单价不能为负数: %', NEW.price_per_meter;
  END IF;
  
  RETURN NEW;
END;
$$;

-- 1.3 验证付款金额一致性
CREATE OR REPLACE FUNCTION validate_payment_amount()
RETURNS TRIGGER LANGUAGE plpgsql AS $$
DECLARE
  v_expected_amount NUMERIC;
  v_tolerance NUMERIC := 0.01;
BEGIN
  -- 计算预期金额 = 数量 × 单价
  v_expected_amount := NEW.quantity * NEW.price_per_meter;
  
  IF ABS(NEW.amount - v_expected_amount) > v_tolerance THEN
    RAISE EXCEPTION 
      '付款金额不一致：期望 %(数量 % × 单价 %)，实际 %',
      v_expected_amount, NEW.quantity, NEW.price_per_meter, NEW.amount;
  END IF;
  
  RETURN NEW;
END;
$$;

-- 1.4 验证应付账款汇总一致性
CREATE OR REPLACE FUNCTION validate_accounts_payable_totals()
RETURNS TRIGGER LANGUAGE plpgsql AS $$
DECLARE
  v_items_total NUMERIC;
  v_items_paid NUMERIC;
  v_tolerance NUMERIC := 0.01;
BEGIN
  -- 从明细表重新计算
  SELECT 
    COALESCE(SUM(amount), 0),
    COALESCE(SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END), 0)
  INTO v_items_total, v_items_paid
  FROM accounts_payable_items
  WHERE accounts_payable_id = NEW.id;
  
  -- 验证总金额
  IF ABS(NEW.total_amount - v_items_total) > v_tolerance THEN
    RAISE EXCEPTION 
      '应付账款总金额不一致：表头 %, 明细合计 %',
      NEW.total_amount, v_items_total;
  END IF;
  
  -- 验证已付金额
  IF ABS(NEW.paid_amount - v_items_paid) > v_tolerance THEN
    RAISE EXCEPTION 
      '应付账款已付金额不一致：表头 %, 明细合计 %',
      NEW.paid_amount, v_items_paid;
  END IF;
  
  -- 验证未付金额
  IF ABS(NEW.unpaid_amount - (v_items_total - v_items_paid)) > v_tolerance THEN
    RAISE EXCEPTION 
      '应付账款未付金额不一致：表头 %, 计算值 %',
      NEW.unpaid_amount, (v_items_total - v_items_paid);
  END IF;
  
  RETURN NEW;
END;
$$;

-- ============================================================================
-- 2. 创建触发器
-- ============================================================================

-- 2.1 计划数量一致性触发器
DROP TRIGGER IF EXISTS trg_validate_plan_quantity ON production_plans;
CREATE TRIGGER trg_validate_plan_quantity
  BEFORE UPDATE ON production_plans
  FOR EACH ROW
  WHEN (OLD.completed_quantity IS DISTINCT FROM NEW.completed_quantity)
  EXECUTE FUNCTION validate_plan_quantity_consistency();

-- 2.2 入库记录正数验证触发器
DROP TRIGGER IF EXISTS trg_validate_inventory_positive ON inventory_records;
CREATE TRIGGER trg_validate_inventory_positive
  BEFORE INSERT OR UPDATE ON inventory_records
  FOR EACH ROW
  EXECUTE FUNCTION validate_inventory_record_positive();

-- 2.3 付款金额验证触发器
DROP TRIGGER IF EXISTS trg_validate_payment ON payments;
CREATE TRIGGER trg_validate_payment
  BEFORE INSERT OR UPDATE ON payments
  FOR EACH ROW
  EXECUTE FUNCTION validate_payment_amount();

-- 2.4 应付账款汇总验证触发器
DROP TRIGGER IF EXISTS trg_validate_accounts_payable ON accounts_payable;
CREATE TRIGGER trg_validate_accounts_payable
  BEFORE INSERT OR UPDATE ON accounts_payable
  FOR EACH ROW
  EXECUTE FUNCTION validate_accounts_payable_totals();

-- ============================================================================
-- 3. 创建审计日志表（记录所有关键数据变更）
-- ============================================================================

CREATE TABLE IF NOT EXISTS data_audit_logs (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  table_name TEXT NOT NULL,
  record_id UUID NOT NULL,
  operation TEXT NOT NULL CHECK (operation IN ('INSERT', 'UPDATE', 'DELETE')),
  old_values JSONB,
  new_values JSONB,
  changed_by UUID REFERENCES auth.users(id),
  changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  validation_passed BOOLEAN NOT NULL DEFAULT TRUE,
  error_message TEXT
);

-- 创建索引加速查询
CREATE INDEX IF NOT EXISTS idx_audit_logs_table_record ON data_audit_logs(table_name, record_id);
CREATE INDEX IF NOT EXISTS idx_audit_logs_changed_at ON data_audit_logs(changed_at DESC);
CREATE INDEX IF NOT EXISTS idx_audit_logs_validation ON data_audit_logs(validation_passed) WHERE validation_passed = FALSE;

-- 启用 RLS
ALTER TABLE data_audit_logs ENABLE ROW LEVEL SECURITY;

-- 审计日志只读策略（仅管理员可查看）
CREATE POLICY admin_select_audit_logs ON data_audit_logs
  FOR SELECT USING (true); -- 暂时开放，后续可改为角色检查

CREATE POLICY system_insert_audit_logs ON data_audit_logs
  FOR INSERT WITH CHECK (true); -- 系统自动写入

-- ============================================================================
-- 4. 创建审计触发器函数
-- ============================================================================

CREATE OR REPLACE FUNCTION log_data_audit()
RETURNS TRIGGER LANGUAGE plpgsql AS $$
DECLARE
  v_old_values JSONB;
  v_new_values JSONB;
  v_operation TEXT;
BEGIN
  v_operation := TG_OP;
  
  IF TG_OP = 'DELETE' THEN
    v_old_values := to_jsonb(OLD);
    v_new_values := NULL;
  ELSIF TG_OP = 'UPDATE' THEN
    v_old_values := to_jsonb(OLD);
    v_new_values := to_jsonb(NEW);
  ELSE -- INSERT
    v_old_values := NULL;
    v_new_values := to_jsonb(NEW);
  END IF;
  
  INSERT INTO data_audit_logs (
    table_name, record_id, operation, old_values, new_values, changed_by
  ) VALUES (
    TG_TABLE_NAME,
    COALESCE(NEW.id, OLD.id),
    v_operation,
    v_old_values,
    v_new_values,
    auth.uid()
  );
  
  IF TG_OP = 'DELETE' THEN
    RETURN OLD;
  ELSE
    RETURN NEW;
  END IF;
END;
$$;

-- ============================================================================
-- 5. 为核心表添加审计触发器
-- ============================================================================

-- 5.1 production_plans 审计
DROP TRIGGER IF EXISTS trg_audit_production_plans ON production_plans;
CREATE TRIGGER trg_audit_production_plans
  AFTER INSERT OR UPDATE OR DELETE ON production_plans
  FOR EACH ROW
  EXECUTE FUNCTION log_data_audit();

-- 5.2 inventory_records 审计
DROP TRIGGER IF EXISTS trg_audit_inventory_records ON inventory_records;
CREATE TRIGGER trg_audit_inventory_records
  AFTER INSERT OR UPDATE OR DELETE ON inventory_records
  FOR EACH ROW
  EXECUTE FUNCTION log_data_audit();

-- 5.3 payments 审计
DROP TRIGGER IF EXISTS trg_audit_payments ON payments;
CREATE TRIGGER trg_audit_payments
  AFTER INSERT OR UPDATE OR DELETE ON payments
  FOR EACH ROW
  EXECUTE FUNCTION log_data_audit();

-- 5.4 accounts_payable 审计
DROP TRIGGER IF EXISTS trg_audit_accounts_payable ON accounts_payable;
CREATE TRIGGER trg_audit_accounts_payable
  AFTER INSERT OR UPDATE OR DELETE ON accounts_payable
  FOR EACH ROW
  EXECUTE FUNCTION log_data_audit();

-- 5.5 accounts_payable_items 审计
DROP TRIGGER IF EXISTS trg_audit_accounts_payable_items ON accounts_payable_items;
CREATE TRIGGER trg_audit_accounts_payable_items
  AFTER INSERT OR UPDATE OR DELETE ON accounts_payable_items
  FOR EACH ROW
  EXECUTE FUNCTION log_data_audit();

-- ============================================================================
-- 6. 创建数据一致性检查视图
-- ============================================================================

-- 6.1 计划数量一致性检查视图
CREATE OR REPLACE VIEW v_plan_quantity_check AS
SELECT 
  p.id as plan_id,
  p.plan_code,
  p.status,
  p.completed_quantity,
  COALESCE(SUM(ir.quantity), 0) as inventory_total,
  p.completed_quantity - COALESCE(SUM(ir.quantity), 0) as difference,
  CASE 
    WHEN ABS(p.completed_quantity - COALESCE(SUM(ir.quantity), 0)) <= 0.01 THEN '✓ 一致'
    ELSE '✗ 不一致'
  END as check_result
FROM production_plans p
LEFT JOIN inventory_records ir ON ir.plan_id = p.id
GROUP BY p.id, p.plan_code, p.status, p.completed_quantity;

-- 6.2 付款金额一致性检查视图
CREATE OR REPLACE VIEW v_payment_check AS
SELECT 
  id as payment_id,
  plan_id,
  quantity,
  price_per_meter,
  amount,
  quantity * price_per_meter as expected_amount,
  amount - (quantity * price_per_meter) as difference,
  CASE 
    WHEN ABS(amount - (quantity * price_per_meter)) <= 0.01 THEN '✓ 一致'
    ELSE '✗ 不一致'
  END as check_result
FROM payments;

-- 6.3 应付账款汇总一致性检查视图
CREATE OR REPLACE VIEW v_accounts_payable_check AS
SELECT 
  ap.id as ap_id,
  ap.plan_id,
  ap.total_amount,
  ap.paid_amount,
  ap.unpaid_amount,
  COALESCE(SUM(api.amount), 0) as items_total,
  COALESCE(SUM(CASE WHEN api.status = 'paid' THEN api.amount ELSE 0 END), 0) as items_paid,
  ap.total_amount - COALESCE(SUM(api.amount), 0) as total_diff,
  ap.paid_amount - COALESCE(SUM(CASE WHEN api.status = 'paid' THEN api.amount ELSE 0 END), 0) as paid_diff,
  CASE 
    WHEN ABS(ap.total_amount - COALESCE(SUM(api.amount), 0)) <= 0.01 
     AND ABS(ap.paid_amount - COALESCE(SUM(CASE WHEN api.status = 'paid' THEN api.amount ELSE 0 END), 0)) <= 0.01
    THEN '✓ 一致'
    ELSE '✗ 不一致'
  END as check_result
FROM accounts_payable ap
LEFT JOIN accounts_payable_items api ON api.accounts_payable_id = ap.id
GROUP BY ap.id, ap.plan_id, ap.total_amount, ap.paid_amount, ap.unpaid_amount;

-- ============================================================================
-- 7. 创建定期一致性检查函数（可由 Edge Function 调用）
-- ============================================================================

CREATE OR REPLACE FUNCTION run_data_integrity_check()
RETURNS TABLE (
  check_type TEXT,
  total_records BIGINT,
  inconsistent_records BIGINT,
  error_rate NUMERIC,
  status TEXT
) LANGUAGE plpgsql AS $$
BEGIN
  -- 检查计划数量一致性
  RETURN QUERY
  SELECT 
    'plan_quantity'::TEXT,
    COUNT(*)::BIGINT,
    SUM(CASE WHEN check_result = '✗ 不一致' THEN 1 ELSE 0 END)::BIGINT,
    ROUND(
      SUM(CASE WHEN check_result = '✗ 不一致' THEN 1 ELSE 0 END)::NUMERIC / 
      NULLIF(COUNT(*), 0) * 100, 4
    ),
    CASE 
      WHEN SUM(CASE WHEN check_result = '✗ 不一致' THEN 1 ELSE 0 END) = 0 THEN 'PASS'
      ELSE 'FAIL'
    END
  FROM v_plan_quantity_check;
  
  -- 检查付款金额一致性
  RETURN QUERY
  SELECT 
    'payment_amount'::TEXT,
    COUNT(*)::BIGINT,
    SUM(CASE WHEN check_result = '✗ 不一致' THEN 1 ELSE 0 END)::BIGINT,
    ROUND(
      SUM(CASE WHEN check_result = '✗ 不一致' THEN 1 ELSE 0 END)::NUMERIC / 
      NULLIF(COUNT(*), 0) * 100, 4
    ),
    CASE 
      WHEN SUM(CASE WHEN check_result = '✗ 不一致' THEN 1 ELSE 0 END) = 0 THEN 'PASS'
      ELSE 'FAIL'
    END
  FROM v_payment_check;
  
  -- 检查应付账款汇总一致性
  RETURN QUERY
  SELECT 
    'accounts_payable'::TEXT,
    COUNT(*)::BIGINT,
    SUM(CASE WHEN check_result = '✗ 不一致' THEN 1 ELSE 0 END)::BIGINT,
    ROUND(
      SUM(CASE WHEN check_result = '✗ 不一致' THEN 1 ELSE 0 END)::NUMERIC / 
      NULLIF(COUNT(*), 0) * 100, 4
    ),
    CASE 
      WHEN SUM(CASE WHEN check_result = '✗ 不一致' THEN 1 ELSE 0 END) = 0 THEN 'PASS'
      ELSE 'FAIL'
    END
  FROM v_accounts_payable_check;
END;
$$;

-- ============================================================================
-- 8. 添加 CHECK 约束（作为最后一道防线）
-- ============================================================================

-- 8.1 入库记录非负约束
ALTER TABLE inventory_records 
  DROP CONSTRAINT IF EXISTS chk_inventory_positive;
ALTER TABLE inventory_records 
  ADD CONSTRAINT chk_inventory_positive 
  CHECK (quantity >= 0 AND rolls >= 0 AND (price_per_meter IS NULL OR price_per_meter >= 0));

-- 8.2 付款金额非负约束
ALTER TABLE payments 
  DROP CONSTRAINT IF EXISTS chk_payment_positive;
ALTER TABLE payments 
  ADD CONSTRAINT chk_payment_positive 
  CHECK (amount >= 0 AND quantity >= 0 AND price_per_meter >= 0);

-- 8.3 应付账款金额逻辑约束
ALTER TABLE accounts_payable 
  DROP CONSTRAINT IF EXISTS chk_ap_amounts;
ALTER TABLE accounts_payable 
  ADD CONSTRAINT chk_ap_amounts 
  CHECK (
    total_amount >= 0 AND 
    paid_amount >= 0 AND 
    unpaid_amount >= 0 AND
    paid_amount <= total_amount AND
    unpaid_amount <= total_amount
  );

-- ============================================================================
-- 9. 创建错误率监控视图
-- ============================================================================

CREATE OR REPLACE VIEW v_error_rate_monitor AS
SELECT 
  DATE_TRUNC('day', changed_at) as check_date,
  table_name,
  COUNT(*) as total_operations,
  SUM(CASE WHEN validation_passed = FALSE THEN 1 ELSE 0 END) as failed_operations,
  ROUND(
    SUM(CASE WHEN validation_passed = FALSE THEN 1 ELSE 0 END)::NUMERIC / 
    NULLIF(COUNT(*), 0) * 10000, 2
  ) as error_rate_per_10k,
  CASE 
    WHEN SUM(CASE WHEN validation_passed = FALSE THEN 1 ELSE 0 END)::NUMERIC / 
         NULLIF(COUNT(*), 0) * 10000 <= 1 THEN '✓ 达标 (≤1/万)'
    ELSE '✗ 超标 (>1/万)'
  END as target_status
FROM data_audit_logs
WHERE changed_at >= NOW() - INTERVAL '30 days'
GROUP BY DATE_TRUNC('day', changed_at), table_name
ORDER BY check_date DESC, table_name;

-- ============================================================================
-- 结束
-- ============================================================================
