-- +goose Up
-- +goose StatementBegin

-- ═══════════════════════════════════════════════════════════════════════════
-- MIGRATION 031 — INTEGRAÇÃO FINANCEIRA: PEDIDOS ↔ LANCAMENTOS
-- ═══════════════════════════════════════════════════════════════════════════

-- 1. Vincula pedidos à tabela de lançamentos
--    Um pedido pode gerar múltiplos lançamentos (entrada + parcelas)
--    mas tem um lançamento "principal" de controle.
ALTER TABLE pedidos
    ADD COLUMN IF NOT EXISTS lancamento_id      UUID REFERENCES lancamentos(id) ON DELETE SET NULL,
    ADD COLUMN IF NOT EXISTS status_financeiro  TEXT NOT NULL DEFAULT 'pendente'
        CHECK (status_financeiro IN ('pendente','parcial','pago','cancelado')),
    ADD COLUMN IF NOT EXISTS valor_pago         NUMERIC(15,4) NOT NULL DEFAULT 0,
    ADD COLUMN IF NOT EXISTS data_vencimento    DATE,
    ADD COLUMN IF NOT EXISTS data_pagamento     DATE;

CREATE INDEX IF NOT EXISTS idx_pedidos_lancamento       ON pedidos (lancamento_id);
CREATE INDEX IF NOT EXISTS idx_pedidos_status_fin       ON pedidos (tenant_id, status_financeiro);
CREATE INDEX IF NOT EXISTS idx_pedidos_data_vencimento  ON pedidos (tenant_id, data_vencimento);

-- 2. View: v_contas_receber
--    Unifica lançamentos manuais + lançamentos de pedidos num único lugar
--    para a aba "Contas a Receber" do módulo financeiro.
CREATE OR REPLACE VIEW v_contas_receber AS
SELECT
    l.id,
    l.tenant_id,
    l.tipo,
    l.categoria,
    l.valor,
    l.descricao,
    l.vencimento,
    l.pago,
    l.pago_em,
    l.referencia,
    l.origem,
    l.origem_id,
    l.forma_pgto,
    l.parcelas,
    l.cliente_id,
    l.criado_em,
    -- Cliente
    COALESCE(cl.nome, '')           AS cliente_nome,
    COALESCE(cl.telefone, '')       AS cliente_telefone,
    COALESCE(cl.documento, '')      AS cliente_documento,
    -- Pedido de origem (quando origem = 'pedido')
    p.numero_os                     AS pedido_numero_os,
    p.situacao                      AS pedido_situacao,
    p.status_financeiro             AS pedido_status_fin,
    p.previsao_entrega              AS pedido_previsao,
    -- Orçamento de origem via propostas (JOIN reverso)
    pr.numero                       AS orcamento_numero,
    -- Dias em atraso
    CASE
        WHEN NOT l.pago AND l.vencimento < CURRENT_DATE
        THEN (CURRENT_DATE - l.vencimento)
        ELSE 0
    END                             AS dias_atraso
FROM lancamentos l
LEFT JOIN clientes  cl ON cl.id = l.cliente_id AND cl.tenant_id = l.tenant_id
LEFT JOIN pedidos   p  ON p.id  = l.origem_id  AND p.tenant_id  = l.tenant_id
                       AND l.origem = 'pedido'
LEFT JOIN propostas pr ON pr.pedido_id = p.id  AND pr.tenant_id = p.tenant_id
                       AND pr.status = 'aprovada'
WHERE l.tipo = 'receita';


-- 3. View: v_pedidos_financeiro
--    Dashboard financeiro dos pedidos: valor, status, vencimento, atraso
CREATE OR REPLACE VIEW v_pedidos_financeiro AS
SELECT
    p.id,
    p.tenant_id,
    p.numero_os,
    p.status,
    p.situacao,
    p.status_financeiro,
    p.total,
    p.desconto,
    p.valor_pago,
    p.total - p.valor_pago                  AS saldo_devedor,
    p.data_vencimento,
    p.data_pagamento,
    p.forma_pagamento,
    p.condicao_pagamento,
    p.parcelas,
    p.previsao_entrega,
    p.lancamento_id,
    -- Orçamento/proposta de origem (JOIN reverso: propostas.pedido_id = p.id)
    pr.numero                               AS orcamento_numero,
    pr.id                                   AS orcamento_id,
    -- Cliente
    COALESCE(cl.nome,      'Sem cliente')   AS cliente_nome,
    COALESCE(cl.telefone,  '')              AS cliente_telefone,
    COALESCE(cl.documento, '')              AS cliente_documento,
    -- Percentual pago
    CASE WHEN p.total > 0
         THEN ROUND((p.valor_pago / p.total) * 100, 1)
         ELSE 0::numeric
    END                                     AS pct_pago,
    -- Dias em atraso
    CASE
        WHEN p.status_financeiro NOT IN ('pago','cancelado')
         AND p.data_vencimento IS NOT NULL
         AND p.data_vencimento < CURRENT_DATE
        THEN (CURRENT_DATE - p.data_vencimento)::int
        ELSE 0
    END                                     AS dias_atraso,
    p.vendedor_nome,
    p.observacoes,
    p.criado_em,
    p.atualizado_em
FROM pedidos p
LEFT JOIN clientes  cl ON cl.id = p.cliente_id AND cl.tenant_id = p.tenant_id
LEFT JOIN propostas pr ON pr.pedido_id = p.id  AND pr.tenant_id = p.tenant_id
                       AND pr.status = 'aprovada';


-- 4. Função: fn_gerar_lancamento_pedido
--    Cria (ou atualiza) um lançamento de receita quando um pedido é
--    confirmado/faturado. Chamada pelo handler Go.
CREATE OR REPLACE FUNCTION fn_gerar_lancamento_pedido(
    p_pedido_id   UUID,
    p_tenant_id   UUID,
    p_user_id     UUID,
    p_vencimento  DATE DEFAULT NULL
)
RETURNS UUID LANGUAGE plpgsql AS $$
DECLARE
    v_lancamento_id UUID;
    v_pedido        RECORD;
    v_cliente_nome  TEXT;
BEGIN
    -- Busca dados do pedido
    SELECT p.*, COALESCE(cl.nome,'') AS cli_nome
    INTO v_pedido
    FROM pedidos p
    LEFT JOIN clientes cl ON cl.id = p.cliente_id
    WHERE p.id = p_pedido_id AND p.tenant_id = p_tenant_id;

    IF NOT FOUND THEN RETURN NULL; END IF;

    -- Se já tem lançamento, apenas atualiza valor
    IF v_pedido.lancamento_id IS NOT NULL THEN
        UPDATE lancamentos
        SET valor       = v_pedido.total,
            vencimento  = COALESCE(p_vencimento, vencimento),
            atualizado_em = now()
        WHERE id = v_pedido.lancamento_id AND tenant_id = p_tenant_id;
        RETURN v_pedido.lancamento_id;
    END IF;

    -- Cria novo lançamento
    v_lancamento_id := uuid_generate_v4();
    INSERT INTO lancamentos
        (id, tenant_id, tipo, categoria, valor, descricao,
         vencimento, pago, origem, origem_id,
         forma_pgto, parcelas, cliente_id, referencia)
    VALUES
        (v_lancamento_id, p_tenant_id, 'receita', 'pedidos',
         v_pedido.total,
         FORMAT('OS#%s — %s', v_pedido.numero_os, v_pedido.cli_nome),
         COALESCE(p_vencimento, CURRENT_DATE + INTERVAL '7 days'),
         false, 'pedido', p_pedido_id,
         COALESCE(NULLIF(v_pedido.forma_pagamento,''), 'outros'),
         COALESCE(NULLIF(v_pedido.parcelas,0), 1),
         v_pedido.cliente_id,
         FORMAT('OS#%s', v_pedido.numero_os));

    -- Vincula pedido ao lançamento
    UPDATE pedidos
    SET lancamento_id     = v_lancamento_id,
        status_financeiro = 'pendente',
        data_vencimento   = COALESCE(p_vencimento, CURRENT_DATE + INTERVAL '7 days')
    WHERE id = p_pedido_id AND tenant_id = p_tenant_id;

    RETURN v_lancamento_id;
END;
$$;


-- 5. Função: fn_registrar_pagamento_pedido
--    Registra recebimento (total ou parcial) no pedido.
--    Sempre cria um lançamento PAGO de recebimento — visível imediatamente
--    no fluxo de caixa e dashboard. O lançamento principal de "a receber"
--    é zerado/baixado apenas quando o pedido é quitado integralmente.
CREATE OR REPLACE FUNCTION fn_registrar_pagamento_pedido(
    p_pedido_id   UUID,
    p_tenant_id   UUID,
    p_valor       NUMERIC(15,4),
    p_forma       TEXT    DEFAULT 'outros',
    p_data        DATE    DEFAULT CURRENT_DATE,
    p_parcelas    INT     DEFAULT 1,
    p_descricao   TEXT    DEFAULT ''
)
RETURNS JSONB LANGUAGE plpgsql AS $$
DECLARE
    v_pedido        RECORD;
    v_novo_pago     NUMERIC(15,4);
    v_status_fin    TEXT;
    v_lanc_rec_id   UUID;
    v_cliente_nome  TEXT;
    v_seq           INT;
BEGIN
    -- Busca pedido com cliente
    SELECT p.*, COALESCE(cl.nome,'') AS cli_nome
    INTO v_pedido
    FROM pedidos p
    LEFT JOIN clientes cl ON cl.id = p.cliente_id
    WHERE p.id = p_pedido_id AND p.tenant_id = p_tenant_id;

    IF NOT FOUND THEN
        RETURN jsonb_build_object('ok', false, 'erro', 'pedido não encontrado');
    END IF;

    -- Calcula sequência de recebimento (1a parcela, 2a parcela...)
    SELECT COUNT(*) + 1 INTO v_seq
    FROM lancamentos
    WHERE origem_id = p_pedido_id
      AND tenant_id = p_tenant_id
      AND pago = true
      AND origem = 'pedido';

    v_novo_pago  := COALESCE(v_pedido.valor_pago, 0) + p_valor;

    -- Status financeiro
    IF v_novo_pago >= v_pedido.total - 0.01 THEN
        v_status_fin := 'pago';
    ELSE
        v_status_fin := 'parcial';
    END IF;

    -- ── Cria lançamento de recebimento (já PAGO, aparece no fluxo imediatamente)
    v_lanc_rec_id := uuid_generate_v4();
    INSERT INTO lancamentos (
        id, tenant_id, tipo, categoria, valor, descricao,
        vencimento, pago, pago_em,
        origem, origem_id,
        forma_pgto, parcelas,
        cliente_id, referencia
    ) VALUES (
        v_lanc_rec_id,
        p_tenant_id,
        'receita',
        'pedidos',
        p_valor,
        CASE
            WHEN p_descricao <> '' THEN p_descricao
            WHEN p_parcelas  > 1   THEN FORMAT('OS#%s — Parcela %s/%s — %s', v_pedido.numero_os, v_seq, p_parcelas, v_pedido.cli_nome)
            WHEN v_status_fin = 'parcial' THEN FORMAT('OS#%s — Receb. parcial %s — %s', v_pedido.numero_os, v_seq, v_pedido.cli_nome)
            ELSE FORMAT('OS#%s — Pagamento final — %s', v_pedido.numero_os, v_pedido.cli_nome)
        END,
        p_data,       -- vencimento = data do recebimento
        true,         -- já pago
        p_data::timestamptz,
        'pedido',
        p_pedido_id,
        p_forma,
        p_parcelas,
        v_pedido.cliente_id,
        FORMAT('OS#%s', v_pedido.numero_os)
    );

    -- ── Atualiza pedido
    UPDATE pedidos
    SET valor_pago        = v_novo_pago,
        status_financeiro = v_status_fin,
        data_pagamento    = CASE WHEN v_status_fin = 'pago' THEN p_data ELSE data_pagamento END
    WHERE id = p_pedido_id AND tenant_id = p_tenant_id;

    -- ── Se quitado: baixa também o lançamento principal de "a receber"
    IF v_status_fin = 'pago' AND v_pedido.lancamento_id IS NOT NULL THEN
        UPDATE lancamentos
        SET pago      = true,
            pago_em   = p_data::timestamptz,
            forma_pgto = p_forma,
            -- Zera valor do lançamento principal para não duplicar no dashboard
            valor     = 0
        WHERE id = v_pedido.lancamento_id AND tenant_id = p_tenant_id;
    END IF;

    RETURN jsonb_build_object(
        'ok',              true,
        'lancamento_id',   v_lanc_rec_id,
        'status_fin',      v_status_fin,
        'valor_pago',      v_novo_pago,
        'saldo_devedor',   GREATEST(0, v_pedido.total - v_novo_pago),
        'seq_recebimento', v_seq
    );
END;
$$;

-- +goose StatementEnd


-- +goose Down
-- +goose StatementBegin

DROP FUNCTION IF EXISTS fn_registrar_pagamento_pedido CASCADE;
DROP FUNCTION IF EXISTS fn_gerar_lancamento_pedido CASCADE;
DROP VIEW IF EXISTS v_pedidos_financeiro CASCADE;
DROP VIEW IF EXISTS v_contas_receber CASCADE;

ALTER TABLE pedidos
    DROP COLUMN IF EXISTS lancamento_id,
    DROP COLUMN IF EXISTS status_financeiro,
    DROP COLUMN IF EXISTS valor_pago,
    DROP COLUMN IF EXISTS data_vencimento,
    DROP COLUMN IF EXISTS data_pagamento;

-- +goose StatementEnd
