-- ============================================================================
-- V36 — SYSCOHADA : Alignement schéma + Seeds plan comptable standard + Trigger
--
-- P0.4 ROADMAP_2026.md — Obligation OHADA SYSCOHADA révisé (applicable depuis 2018)
-- Corrige l'écart entre V1 (schéma minimal) et les entités Java (colonnes Hibernate).
-- Ajoute le plan comptable standard SYSCOHADA pour mutuelles/coopératives UEMOA.
-- ============================================================================

-- ============================================================================
-- 1. COMPTES_COMPTABLES — Alignement colonnes V1 → entité Java
-- ============================================================================

-- La V1 crée la table avec numero/libelle/type_compte/organisation_id seulement.
-- L'entité Java attend : numero_compte, classe_comptable, solde_initial, solde_actuel,
--   compte_collectif, compte_analytique, cree_par, modifie_par.

-- Renommer la colonne numero → numero_compte si elle n'a pas déjà été renommée par Hibernate
-- Sinon : si les deux colonnes coexistent (Hibernate a créé numero_compte, V1 a laissé numero),
-- on supprime l'ancienne colonne obsolète numero (NOT NULL sans défaut, bloque les INSERTs).
DO $$
BEGIN
    IF EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_name = 'comptes_comptables' AND column_name = 'numero'
    ) THEN
        IF NOT EXISTS (
            SELECT 1 FROM information_schema.columns
            WHERE table_name = 'comptes_comptables' AND column_name = 'numero_compte'
        ) THEN
            ALTER TABLE comptes_comptables RENAME COLUMN numero TO numero_compte;
        ELSE
            -- Les deux colonnes coexistent : recopier les valeurs vers numero_compte si besoin,
            -- puis supprimer la colonne obsolète numero.
            UPDATE comptes_comptables SET numero_compte = numero
                WHERE numero_compte IS NULL AND numero IS NOT NULL;
            ALTER TABLE comptes_comptables DROP COLUMN numero;
        END IF;
    END IF;
END $$;

-- Ajouter colonnes manquantes si pas encore créées par Hibernate update
ALTER TABLE comptes_comptables
    ADD COLUMN IF NOT EXISTS classe_comptable INTEGER,
    ADD COLUMN IF NOT EXISTS solde_initial DECIMAL(14,2) DEFAULT 0,
    ADD COLUMN IF NOT EXISTS solde_actuel DECIMAL(14,2) DEFAULT 0,
    ADD COLUMN IF NOT EXISTS compte_collectif BOOLEAN DEFAULT false,
    ADD COLUMN IF NOT EXISTS compte_analytique BOOLEAN DEFAULT false,
    ADD COLUMN IF NOT EXISTS description VARCHAR(500),
    ADD COLUMN IF NOT EXISTS cree_par VARCHAR(255),
    ADD COLUMN IF NOT EXISTS modifie_par VARCHAR(255);

-- Déduire classe_comptable depuis numero_compte si null (première chiffre du numéro)
UPDATE comptes_comptables
SET classe_comptable = CAST(LEFT(numero_compte, 1) AS INTEGER)
WHERE classe_comptable IS NULL AND numero_compte IS NOT NULL AND LENGTH(numero_compte) > 0;

-- Rendre classe_comptable NOT NULL après backfill
DO $$
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_name = 'comptes_comptables' AND column_name = 'classe_comptable'
          AND is_nullable = 'NO'
    ) THEN
        ALTER TABLE comptes_comptables ALTER COLUMN classe_comptable SET NOT NULL;
    END IF;
END $$;

-- Contrainte classe 1-9 (SYSCOHADA a 9 classes, pas 7)
DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_compte_classe_syscohada') THEN
        ALTER TABLE comptes_comptables
            ADD CONSTRAINT chk_compte_classe_syscohada
            CHECK (classe_comptable >= 1 AND classe_comptable <= 9);
    END IF;
END $$;

-- ============================================================================
-- 2. JOURNAUX_COMPTABLES — Alignement colonnes
-- ============================================================================
ALTER TABLE journaux_comptables
    ADD COLUMN IF NOT EXISTS date_debut DATE,
    ADD COLUMN IF NOT EXISTS date_fin DATE,
    ADD COLUMN IF NOT EXISTS statut VARCHAR(20) DEFAULT 'OUVERT',
    ADD COLUMN IF NOT EXISTS description VARCHAR(500),
    ADD COLUMN IF NOT EXISTS cree_par VARCHAR(255),
    ADD COLUMN IF NOT EXISTS modifie_par VARCHAR(255);

-- ============================================================================
-- 3. ECRITURES_COMPTABLES — Alignement colonnes
-- ============================================================================
ALTER TABLE ecritures_comptables
    ADD COLUMN IF NOT EXISTS organisation_id UUID REFERENCES organisations(id),
    ADD COLUMN IF NOT EXISTS paiement_id UUID REFERENCES paiements(id),
    ADD COLUMN IF NOT EXISTS reference VARCHAR(100),
    ADD COLUMN IF NOT EXISTS lettrage VARCHAR(20),
    ADD COLUMN IF NOT EXISTS pointe BOOLEAN DEFAULT false,
    ADD COLUMN IF NOT EXISTS montant_debit DECIMAL(14,2) DEFAULT 0,
    ADD COLUMN IF NOT EXISTS montant_credit DECIMAL(14,2) DEFAULT 0,
    ADD COLUMN IF NOT EXISTS commentaire VARCHAR(1000),
    ADD COLUMN IF NOT EXISTS cree_par VARCHAR(255),
    ADD COLUMN IF NOT EXISTS modifie_par VARCHAR(255);

-- ============================================================================
-- 4. LIGNES_ECRITURE — Alignement colonnes (debit/credit → montant_debit/credit)
-- ============================================================================

-- Renommer compte_id → compte_comptable_id si besoin
DO $$
BEGIN
    IF EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_name = 'lignes_ecriture' AND column_name = 'compte_id'
    ) AND NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_name = 'lignes_ecriture' AND column_name = 'compte_comptable_id'
    ) THEN
        ALTER TABLE lignes_ecriture RENAME COLUMN compte_id TO compte_comptable_id;
    END IF;
END $$;

-- Renommer debit/credit → montant_debit/montant_credit si besoin
DO $$
BEGIN
    IF EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_name = 'lignes_ecriture' AND column_name = 'debit'
    ) AND NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_name = 'lignes_ecriture' AND column_name = 'montant_debit'
    ) THEN
        ALTER TABLE lignes_ecriture RENAME COLUMN debit TO montant_debit;
    END IF;
END $$;

DO $$
BEGIN
    IF EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_name = 'lignes_ecriture' AND column_name = 'credit'
    ) AND NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_name = 'lignes_ecriture' AND column_name = 'montant_credit'
    ) THEN
        ALTER TABLE lignes_ecriture RENAME COLUMN credit TO montant_credit;
    END IF;
END $$;

ALTER TABLE lignes_ecriture
    ADD COLUMN IF NOT EXISTS numero_ligne INTEGER,
    ADD COLUMN IF NOT EXISTS reference VARCHAR(100),
    ADD COLUMN IF NOT EXISTS cree_par VARCHAR(255),
    ADD COLUMN IF NOT EXISTS modifie_par VARCHAR(255);

-- ============================================================================
-- 5. TABLE MODELE_PLAN_COMPTABLE — Template SYSCOHADA (comptes standards réutilisables)
-- ============================================================================
CREATE TABLE IF NOT EXISTS modele_plan_comptable (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    numero_compte VARCHAR(10) NOT NULL UNIQUE,
    libelle VARCHAR(200) NOT NULL,
    classe_comptable INTEGER NOT NULL CHECK (classe_comptable >= 1 AND classe_comptable <= 9),
    type_compte VARCHAR(30) NOT NULL,
    description VARCHAR(500),
    actif BOOLEAN NOT NULL DEFAULT TRUE,
    CONSTRAINT chk_modele_classe CHECK (classe_comptable >= 1 AND classe_comptable <= 9)
);

-- ============================================================================
-- 6. SEEDS — Plan comptable SYSCOHADA standard pour mutuelles/coopératives UEMOA
-- ============================================================================
INSERT INTO modele_plan_comptable (numero_compte, libelle, classe_comptable, type_compte) VALUES
-- CLASSE 1 — Ressources durables
('101000', 'Fonds propres', 1, 'PASSIF'),
('104000', 'Réserve légale', 1, 'PASSIF'),
('106000', 'Réserves statutaires', 1, 'PASSIF'),
('120000', 'Résultat de l''exercice', 1, 'PASSIF'),
('160000', 'Emprunts à long terme', 1, 'PASSIF'),
('165000', 'Dépôts et cautionnements reçus', 1, 'PASSIF'),

-- CLASSE 2 — Actif immobilisé
('222000', 'Matériel de transport', 2, 'ACTIF'),
('232000', 'Matériel informatique', 2, 'ACTIF'),
('244000', 'Logiciels informatiques', 2, 'ACTIF'),
('281000', 'Amortissements immobilisations', 2, 'ACTIF'),

-- CLASSE 4 — Tiers
('411000', 'Membres débiteurs — cotisations dues', 4, 'ACTIF'),
('412000', 'Membres débiteurs — parts sociales dues', 4, 'ACTIF'),
('413000', 'Membres débiteurs — avances sur prestations', 4, 'ACTIF'),
('421000', 'Personnel — rémunérations dues', 4, 'PASSIF'),
('431000', 'Sécurité sociale — cotisations patronales', 4, 'PASSIF'),
('441000', 'État — TVA collectée', 4, 'PASSIF'),
('447000', 'État — autres impôts et taxes', 4, 'PASSIF'),
('467000', 'Tiers divers débiteurs', 4, 'ACTIF'),
('468000', 'Tiers divers créditeurs', 4, 'PASSIF'),

-- CLASSE 5 — Trésorerie
('512100', 'Compte Wave Senegal', 5, 'TRESORERIE'),
('512200', 'Compte Orange Money', 5, 'TRESORERIE'),
('512300', 'Compte MTN MoMo', 5, 'TRESORERIE'),
('512400', 'Compte Moov Money', 5, 'TRESORERIE'),
('512500', 'Compte bancaire principal', 5, 'TRESORERIE'),
('531000', 'Caisse principale', 5, 'TRESORERIE'),
('581000', 'Virements internes de trésorerie', 5, 'TRESORERIE'),

-- CLASSE 6 — Charges
('601000', 'Achats de marchandises', 6, 'CHARGES'),
('611000', 'Transports', 6, 'CHARGES'),
('612000', 'Frais de télécommunications', 6, 'CHARGES'),
('613000', 'Frais d''assurance', 6, 'CHARGES'),
('614000', 'Location matériel', 6, 'CHARGES'),
('616000', 'Frais d''entretien et réparations', 6, 'CHARGES'),
('621000', 'Personnel externe (prestataires)', 6, 'CHARGES'),
('622000', 'Rémunérations du personnel', 6, 'CHARGES'),
('631000', 'Frais financiers — intérêts d''emprunts', 6, 'CHARGES'),
('641000', 'Charges sur prestations mutuelles', 6, 'CHARGES'),
('651000', 'Pertes sur créances irrécouvrables', 6, 'CHARGES'),

-- CLASSE 7 — Produits
('706100', 'Cotisations ordinaires membres', 7, 'PRODUITS'),
('706200', 'Cotisations spéciales / majorées', 7, 'PRODUITS'),
('706300', 'Parts sociales', 7, 'PRODUITS'),
('706400', 'Droits d''adhésion', 7, 'PRODUITS'),
('762000', 'Produits financiers — intérêts épargne', 7, 'PRODUITS'),
('771000', 'Subventions d''exploitation reçues', 7, 'PRODUITS'),
('775000', 'Prestations de services', 7, 'PRODUITS'),

-- CLASSE 8 — Charges et produits exceptionnels / hors activité
('870000', 'Dons reçus', 8, 'PRODUITS'),
('871000', 'Legs et donations', 8, 'PRODUITS'),
('875000', 'Produits exceptionnels d''événements', 8, 'PRODUITS'),
('878000', 'Autres produits hors activité ordinaire', 8, 'PRODUITS'),
('880000', 'Charges exceptionnelles', 8, 'CHARGES'),

-- CLASSE 9 — Engagements / comptabilité analytique
('990000', 'Engagements hors bilan donnés', 9, 'AUTRE'),
('991000', 'Engagements hors bilan reçus', 9, 'AUTRE')

ON CONFLICT (numero_compte) DO NOTHING;

-- ============================================================================
-- 7. TRIGGER — Initialisation automatique du plan comptable à la création d'org
-- ============================================================================
CREATE OR REPLACE FUNCTION init_plan_comptable_organisation()
RETURNS TRIGGER AS $$
BEGIN
    INSERT INTO comptes_comptables (
        id, numero_compte, libelle, classe_comptable, type_compte,
        description, organisation_id, solde_initial, solde_actuel,
        compte_collectif, compte_analytique, actif,
        date_creation, version
    )
    SELECT
        gen_random_uuid(),
        m.numero_compte,
        m.libelle,
        m.classe_comptable,
        m.type_compte,
        m.description,
        NEW.id,
        0, 0,
        false, false, true,
        NOW(), 0
    FROM modele_plan_comptable m
    WHERE m.actif = true;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DO $$
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM pg_trigger
        WHERE tgname = 'trg_init_plan_comptable_org'
          AND tgrelid = 'organisations'::regclass
    ) THEN
        CREATE TRIGGER trg_init_plan_comptable_org
        AFTER INSERT ON organisations
        FOR EACH ROW
        EXECUTE FUNCTION init_plan_comptable_organisation();
    END IF;
END $$;

-- ============================================================================
-- 8. BACKFILL — Initialiser le plan comptable pour les organisations existantes
--    (qui ont été créées avant ce trigger)
-- ============================================================================
INSERT INTO comptes_comptables (
    id, numero_compte, libelle, classe_comptable, type_compte,
    description, organisation_id, solde_initial, solde_actuel,
    compte_collectif, compte_analytique, actif,
    date_creation, version
)
SELECT
    gen_random_uuid(),
    m.numero_compte,
    m.libelle,
    m.classe_comptable,
    m.type_compte,
    m.description,
    o.id,
    0, 0,
    false, false, true,
    NOW(), 0
FROM organisations o
CROSS JOIN modele_plan_comptable m
WHERE m.actif = true
  AND NOT EXISTS (
    SELECT 1 FROM comptes_comptables cc
    WHERE cc.organisation_id = o.id
      AND cc.numero_compte = m.numero_compte
  );

-- ============================================================================
-- 9. JOURNAUX STANDARD par organisation
-- ============================================================================

-- Remplacer la contrainte UNIQUE globale sur `code` par une contrainte composite
-- (organisation_id, code) — plusieurs orgs peuvent avoir un journal ACH/VTE/etc.
DO $$
DECLARE
    constraint_name text;
BEGIN
    SELECT tc.constraint_name INTO constraint_name
    FROM information_schema.table_constraints tc
    JOIN information_schema.constraint_column_usage ccu
        ON tc.constraint_name = ccu.constraint_name
    WHERE tc.table_name = 'journaux_comptables'
      AND tc.constraint_type = 'UNIQUE'
      AND ccu.column_name = 'code'
      AND NOT EXISTS (
        SELECT 1 FROM information_schema.constraint_column_usage ccu2
        WHERE ccu2.constraint_name = tc.constraint_name
          AND ccu2.column_name = 'organisation_id'
      );
    IF constraint_name IS NOT NULL THEN
        EXECUTE 'ALTER TABLE journaux_comptables DROP CONSTRAINT ' || quote_ident(constraint_name);
    END IF;
END $$;

DO $$
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM pg_constraint
        WHERE conname = 'uk_journaux_org_code'
    ) THEN
        ALTER TABLE journaux_comptables
            ADD CONSTRAINT uk_journaux_org_code UNIQUE (organisation_id, code);
    END IF;
END $$;

INSERT INTO journaux_comptables (
    id, code, libelle, type_journal, organisation_id,
    statut, actif, date_creation, version
)
SELECT
    gen_random_uuid(),
    jtype.code,
    jtype.libelle,
    jtype.type_journal,
    o.id,
    'OUVERT', true, NOW(), 0
FROM organisations o
CROSS JOIN (VALUES
    ('ACH', 'Journal des achats', 'ACHATS'),
    ('VTE', 'Journal des ventes / cotisations', 'VENTES'),
    ('BQ',  'Journal bancaire', 'BANQUE'),
    ('CAI', 'Journal de caisse', 'CAISSE'),
    ('OD',  'Journal des opérations diverses', 'OD')
) AS jtype(code, libelle, type_journal)
WHERE NOT EXISTS (
    SELECT 1 FROM journaux_comptables jc
    WHERE jc.organisation_id = o.id
      AND jc.type_journal = jtype.type_journal
);

-- ============================================================================
-- 10. INDEX utiles
-- ============================================================================
CREATE INDEX IF NOT EXISTS idx_comptes_org_numero
    ON comptes_comptables (organisation_id, numero_compte);

CREATE INDEX IF NOT EXISTS idx_comptes_org_classe
    ON comptes_comptables (organisation_id, classe_comptable);

CREATE INDEX IF NOT EXISTS idx_ecritures_org_date
    ON ecritures_comptables (organisation_id, date_ecriture);

CREATE INDEX IF NOT EXISTS idx_lignes_compte
    ON lignes_ecriture (compte_comptable_id);
