<?php
/* Conciliación Yape persistente (AGESCONT) sobre MySQL + sesión.
   importar (POST pdf) -> acumula ingresos (idempotente por yape_id)
   estado (GET) -> resumen + ingresos pendientes con propuestas
   vincular (POST {pagoPk,yapeId,clienteId,pagadorNorm}) -> marca pago Pagado + ingreso conciliado + aprende alias
   ignorar (POST {yapeIds}) / desvincular (POST {yapeId})
   sugerenciasPago (GET pk) -> ingresos que podrían pagar ESE pago (para conciliar desde el cliente) */
session_start();
header('Content-Type: application/json; charset=utf-8');
$cfg = require __DIR__ . '/db_config.php';
$pdo = new PDO("mysql:host={$cfg['host']};dbname={$cfg['dbname']};charset=utf8mb4", $cfg['user'], $cfg['pass'], [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]);
function resp($ok, $data = null, $msg = '', $code = 200) { http_response_code($code); echo json_encode(['success'=>$ok,'data'=>$data,'message'=>$msg], JSON_UNESCAPED_UNICODE); exit; }
if (empty($_SESSION['uid'])) resp(false, null, 'No autenticado', 401);
function inp() { return json_decode(file_get_contents('php://input'), true); }

$YAPE_STOP = ['SRL','SR','LTDA','CIA','COMPANY','COMPANIA','EMPRESARIALES','SERVICE','LOGISTIC','AND','DEL','LOS','LAS'];
function ynorm($s) { $s = @iconv('UTF-8','ASCII//TRANSLIT//IGNORE',$s); $s = preg_replace('/[^A-Za-z0-9 ]/',' ',$s); return strtoupper(trim(preg_replace('/\s+/',' ',$s))); }
function ytokens($s) { global $YAPE_STOP; $o=[]; foreach (explode(' ', ynorm($s)) as $t) { if (strlen($t)>=3 && !in_array($t,$YAPE_STOP)) $o[$t]=1; } return array_keys($o); }
function yamount($s) { return floatval(str_replace(',','.',str_replace('.','',$s))); }
function ypayer($d) { return trim(preg_replace('/^[A-Z]{2,5}\s*-\s*/','',trim($d))); }
function yfiso($f) { $x=explode('/',$f); return count($x)==3? sprintf('%s-%s-%s',$x[2],$x[1],$x[0]):$f; }
function yparse($text) {
    $rows=[]; $idx=-1;
    foreach (explode("\n",$text) as $line) {
        $t=trim($line); if ($t==='') continue;
        if (preg_match('#^(\d{2}/\d{2}/\d{4})\s+(\d{2}:\d{2}:\d{2})\s+(\d+)\s+(.*?)\s+([+\-])\s*Bs\s*([\d.,]+)\s+Bs\s*([\d.,]+)$#u',$t,$m)) {
            $mid=trim($m[4]); $parts=preg_split('/\s{2,}/',$mid);
            $rows[]=['fecha'=>$m[1],'hora'=>$m[2],'yapeId'=>$m[3],'desc'=>$parts[0]??'','detalle'=>count($parts)>1?implode(' ',array_slice($parts,1)):'','sign'=>$m[5],'monto'=>yamount($m[6])];
            $idx=count($rows)-1;
        } elseif ($idx>=0 && strpos($t,'Bs')===false && stripos($t,'verific')===false && stripos($t,'Direcc')===false && stripos($t,'supervis')===false && stripos($t,'ayuda')===false) {
            $cont=preg_split('/\s{2,}/',$t);
            $rows[$idx]['desc']=trim($rows[$idx]['desc'].' '.($cont[0]??''));
            if (count($cont)>1) $rows[$idx]['detalle']=trim($rows[$idx]['detalle'].' '.implode(' ',array_slice($cont,1)));
        }
    }
    return $rows;
}
function buildIdx($pdo) {
    $clientes=$pdo->query("SELECT id,razon_social FROM clientes")->fetchAll();
    $cliTokens=[]; $df=[]; $cliName=[];
    foreach($clientes as $c){ $tk=ytokens($c['razon_social']); $cliTokens[$c['id']]=$tk; $cliName[$c['id']]=$c['razon_social']; foreach($tk as $t)$df[$t]=($df[$t]??0)+1; }
    $aliases=[]; foreach($pdo->query("SELECT pagador_norm,cliente_id FROM yape_aliases") as $a)$aliases[$a['pagador_norm']]=$a['cliente_id'];
    return [$clientes,$cliTokens,$df,$cliName,$aliases];
}
function matchIngreso($pn,$pnNorm,$amt,$pend,$cliTokens,$df,$cliName,$aliases) {
    $ptoks=ytokens($pn);
    $prop=['estado'=>'sin','clienteId'=>null,'clienteNombre'=>'','pagoPk'=>null,'concepto'=>'','incluir'=>false,'nCandMonto'=>0];
    $cid=$aliases[$pnNorm]??null; $viaAlias=$cid!==null; $best=null;
    if($cid===null){
        foreach($cliTokens as $kcid=>$tk){ if(!$tk)continue; $shared=array_values(array_intersect($ptoks,$tk)); if(!$shared)continue;
            $strong=array_filter($shared,fn($t)=>($df[$t]??99)<=3);
            if(count($strong)>=1||count($shared)>=3){ $sc=0; foreach($shared as $t)$sc+=1.0/max(1,($df[$t]??1)); if($best===null||$sc>$best['score'])$best=['cid'=>$kcid,'score'=>$sc,'shared'=>count($shared)]; }
        }
        if($best)$cid=$best['cid'];
    }
    $sharedBest=$viaAlias?99:($best['shared']??0);
    $pendAmt=array_values(array_filter($pend,fn($p)=>abs((float)$p['monto']-$amt)<0.01)); $prop['nCandMonto']=count($pendAmt);
    if($cid!==null){
        $prop['clienteId']=$cid; $prop['clienteNombre']=$cliName[$cid]??'';
        $cpend=array_values(array_filter($pend,fn($p)=>$p['cliente_id']==$cid));
        $exact=array_values(array_filter($cpend,fn($p)=>abs((float)$p['monto']-$amt)<0.01));
        $pick=$exact?$exact[0]:($cpend?$cpend[0]:null);
        if($pick){$prop['pagoPk']=(int)$pick['pk'];$prop['concepto']=$pick['concepto'];}
        if($exact&&$viaAlias){$prop['estado']='auto';$prop['incluir']=true;}
        elseif($exact&&$sharedBest>=2){$prop['estado']='alta';$prop['incluir']=true;}
        else{$prop['estado']='media';}
    } else {
        if(count($pendAmt)===1){$prop['estado']='media_monto';$prop['pagoPk']=(int)$pendAmt[0]['pk'];$prop['clienteId']=$pendAmt[0]['cliente_id'];$prop['clienteNombre']=$cliName[$pendAmt[0]['cliente_id']]??'';$prop['concepto']=$pendAmt[0]['concepto'];}
        elseif(count($pendAmt)>1){$prop['estado']='ambiguo';}
    }
    return $prop;
}

$action = $_GET['action'] ?? '';

if ($action === 'importar') {
    if (empty($_FILES['pdf']['tmp_name']) || !is_uploaded_file($_FILES['pdf']['tmp_name'])) resp(false, null, 'No se recibió el PDF', 400);
    $text = shell_exec('pdftotext -layout ' . escapeshellarg($_FILES['pdf']['tmp_name']) . ' - 2>/dev/null');
    if (!$text) resp(false, null, 'No se pudo leer el PDF', 500);
    $cuenta = preg_match('/Nro\.?\s*de\s*cuenta:\s*(\d+)/i', $text, $mc) ? $mc[1] : null;
    $ingresos = array_values(array_filter(yparse($text), fn($r) => $r['sign'] === '+'));
    $st = $pdo->prepare("INSERT IGNORE INTO yape_ingresos (yape_id,fecha,hora,pagador,pagador_norm,detalle,monto,cuenta) VALUES (?,?,?,?,?,?,?,?)");
    $nuevos = 0;
    foreach ($ingresos as $r) { $pn = ypayer($r['desc']); $st->execute([$r['yapeId'], yfiso($r['fecha']), $r['hora'], $pn, ynorm($pn), $r['detalle'], $r['monto'], $cuenta]); $nuevos += $st->rowCount(); }
    resp(true, ['procesados'=>count($ingresos),'nuevos'=>$nuevos,'duplicados'=>count($ingresos)-$nuevos,'cuenta'=>$cuenta], 'Importado');
}

elseif ($action === 'estado') {
    $pdo->exec("UPDATE yape_ingresos yi JOIN pagos p ON p.ref_yape=yi.yape_id SET yi.estado='conciliado', yi.pago_pk=p.pk WHERE yi.estado<>'conciliado'");
    $counts = $pdo->query("SELECT estado, COUNT(*) c FROM yape_ingresos GROUP BY estado")->fetchAll(PDO::FETCH_KEY_PAIR);
    list($clientes,$cliTokens,$df,$cliName,$aliases) = buildIdx($pdo);
    $pend = $pdo->query("SELECT pk,cliente_id,concepto,monto,fecha_vencimiento FROM pagos WHERE estado='Pendiente'")->fetchAll();
    $pendIng = $pdo->query("SELECT * FROM yape_ingresos WHERE estado='pendiente' ORDER BY fecha DESC, monto DESC")->fetchAll();
    $out = [];
    foreach ($pendIng as $ing) {
        $prop = matchIngreso($ing['pagador'], $ing['pagador_norm'], (float)$ing['monto'], $pend, $cliTokens, $df, $cliName, $aliases);
        $out[] = array_merge(['yapeId'=>$ing['yape_id'],'fecha'=>$ing['fecha'],'pagador'=>$ing['pagador'],'pagadorNorm'=>$ing['pagador_norm'],'monto'=>(float)$ing['monto'],'detalle'=>$ing['detalle']], $prop);
    }
    $cliOut = []; foreach ($clientes as $c) $cliOut[] = ['id'=>$c['id'],'nombre'=>$c['razon_social']]; usort($cliOut, fn($a,$b)=>strcmp($a['nombre'],$b['nombre']));
    $pendOut = []; foreach ($pend as $p) $pendOut[] = ['pk'=>(int)$p['pk'],'clienteId'=>$p['cliente_id'],'monto'=>(float)$p['monto'],'concepto'=>$p['concepto'],'fechaVencimiento'=>$p['fecha_vencimiento']];
    resp(true, ['counts'=>['pendiente'=>(int)($counts['pendiente']??0),'conciliado'=>(int)($counts['conciliado']??0),'ignorado'=>(int)($counts['ignorado']??0)],'pendientes'=>$out,'clientes'=>$cliOut,'pagosPendientes'=>$pendOut], 'ok');
}

elseif ($action === 'vincular') {
    $in = inp(); $pagoPk = (int)($in['pagoPk'] ?? 0); $yapeId = $in['yapeId'] ?? ''; $cid = $in['clienteId'] ?? null; $pn = $in['pagadorNorm'] ?? '';
    if (!$pagoPk || !$yapeId) resp(false, null, 'Faltan datos', 400);
    $ing = $pdo->prepare("SELECT fecha FROM yape_ingresos WHERE yape_id=?"); $ing->execute([$yapeId]); $ir = $ing->fetch();
    $fecha = $ir ? $ir['fecha'] : null;
    $pdo->beginTransaction();
    $pdo->prepare("UPDATE pagos SET estado='Pagado',metodo_pago='Yape',fecha=?,ref_yape=?,data=JSON_SET(data,'$.estado','Pagado','$.metodoPago','Yape','$.fecha',?,'$.refYape',?) WHERE pk=?")->execute([$fecha,$yapeId,$fecha,$yapeId,$pagoPk]);
    $pdo->prepare("UPDATE yape_ingresos SET estado='conciliado',pago_pk=? WHERE yape_id=?")->execute([$pagoPk,$yapeId]);
    if ($cid && $pn) $pdo->prepare("INSERT INTO yape_aliases (pagador_norm,cliente_id) VALUES (?,?) ON DUPLICATE KEY UPDATE cliente_id=VALUES(cliente_id)")->execute([$pn,$cid]);
    $pdo->commit();
    resp(true, null, 'Vinculado');
}

elseif ($action === 'ignorar') {
    $ids = array_values(array_filter((inp()['yapeIds'] ?? []))); if (!$ids) resp(false, null, 'Sin selección', 400);
    $ph = implode(',', array_fill(0, count($ids), '?'));
    $pdo->prepare("UPDATE yape_ingresos SET estado='ignorado' WHERE yape_id IN ($ph)")->execute($ids);
    resp(true, ['afectados'=>count($ids)], 'Ignorados');
}

elseif ($action === 'desvincular') {
    $yapeId = inp()['yapeId'] ?? ''; if (!$yapeId) resp(false, null, 'Falta yapeId', 400);
    $pdo->beginTransaction();
    // cargos pre-existentes -> vuelven a Pendiente ; los creados desde Yape -> se eliminan
    $pdo->prepare("UPDATE pagos SET estado='Pendiente',metodo_pago='',ref_yape=NULL,data=JSON_SET(data,'$.estado','Pendiente','$.metodoPago','','$.refYape',NULL) WHERE ref_yape=? AND (id IS NULL OR id NOT LIKE 'YAPE-%')")->execute([$yapeId]);
    $pdo->prepare("DELETE FROM pagos WHERE ref_yape=? AND id LIKE 'YAPE-%'")->execute([$yapeId]);
    $pdo->prepare("UPDATE yape_ingresos SET estado='pendiente',pago_pk=NULL WHERE yape_id=?")->execute([$yapeId]);
    $pdo->commit();
    resp(true, null, 'Desvinculado');
}

elseif ($action === 'vincularMulti') {
    // UN pago de Yape que salda VARIOS cargos (y/o crea nuevos). El total debe cuadrar con el monto del Yape.
    $in = inp(); $yapeId = $in['yapeId'] ?? ''; $cid = $in['clienteId'] ?? null; $pn = $in['pagadorNorm'] ?? '';
    $pagoPks = array_values(array_filter(array_map('intval', $in['pagoPks'] ?? [])));
    $nuevos = is_array($in['nuevos'] ?? null) ? $in['nuevos'] : [];
    if (!$yapeId) resp(false, null, 'Falta yapeId', 400);
    if (!$pagoPks && !$nuevos) resp(false, null, 'Sin cargos seleccionados', 400);
    if ($nuevos && !$cid) resp(false, null, 'Para registrar pagos nuevos elegí el cliente', 400);
    $r = $pdo->prepare("SELECT fecha, monto FROM yape_ingresos WHERE yape_id=?"); $r->execute([$yapeId]); $ir = $r->fetch();
    if (!$ir) resp(false, null, 'Ingreso no existe', 404);
    $fecha = $ir['fecha'];
    $pdo->beginTransaction();
    $firstPk = null;
    if ($pagoPks) {
        $ph = implode(',', array_fill(0, count($pagoPks), '?'));
        $pdo->prepare("UPDATE pagos SET estado='Pagado',metodo_pago='Yape',fecha=?,ref_yape=?,data=JSON_SET(data,'$.estado','Pagado','$.metodoPago','Yape','$.fecha',?,'$.refYape',?) WHERE pk IN ($ph)")->execute(array_merge([$fecha,$yapeId,$fecha,$yapeId], $pagoPks));
        $firstPk = $pagoPks[0];
    }
    foreach ($nuevos as $nv) {
        $concepto = trim($nv['concepto'] ?? '') ?: 'Pago Yape'; $monto = (float)($nv['monto'] ?? 0);
        $idPago = 'YAPE-' . $yapeId . '-' . substr(md5($concepto . $monto . microtime(true)), 0, 6);
        $data = json_encode(['id'=>$idPago,'clienteId'=>(int)$cid,'concepto'=>$concepto,'monto'=>$monto,'estado'=>'Pagado','metodoPago'=>'Yape','fecha'=>$fecha,'fechaVencimiento'=>$fecha,'refYape'=>$yapeId,'notas'=>'Creado desde conciliación Yape (multi)'], JSON_UNESCAPED_UNICODE);
        $pdo->prepare("INSERT INTO pagos (id,cliente_id,concepto,monto,estado,fecha,fecha_vencimiento,metodo_pago,ref_yape,data) VALUES (?,?,?,?,?,?,?,?,?,?)")->execute([$idPago,$cid,$concepto,$monto,'Pagado',$fecha,$fecha,'Yape',$yapeId,$data]);
        if ($firstPk === null) $firstPk = (int)$pdo->lastInsertId();
    }
    $pdo->prepare("UPDATE yape_ingresos SET estado='conciliado', pago_pk=? WHERE yape_id=?")->execute([$firstPk, $yapeId]);
    if ($cid && $pn) $pdo->prepare("INSERT INTO yape_aliases (pagador_norm,cliente_id) VALUES (?,?) ON DUPLICATE KEY UPDATE cliente_id=VALUES(cliente_id)")->execute([$pn, $cid]);
    $pdo->commit();
    resp(true, ['cargos'=>count($pagoPks)+count($nuevos)], 'Conciliado');
}

elseif ($action === 'sugerenciasPago') {
    $pk = (int)($_GET['pk'] ?? 0); if (!$pk) resp(false, null, 'Falta pk', 400);
    $pr = $pdo->prepare("SELECT cliente_id,monto FROM pagos WHERE pk=?"); $pr->execute([$pk]); $pago = $pr->fetch();
    if (!$pago) resp(false, null, 'Pago no existe', 404);
    list($clientes,$cliTokens,$df,$cliName,$aliases) = buildIdx($pdo);
    $cliToks = $cliTokens[$pago['cliente_id']] ?? [];
    $pendIng = $pdo->query("SELECT * FROM yape_ingresos WHERE estado='pendiente' ORDER BY fecha DESC")->fetchAll();
    $sug = [];
    foreach ($pendIng as $ing) {
        $amtMatch = abs((float)$ing['monto'] - (float)$pago['monto']) < 0.01;
        $aliasMatch = (($aliases[$ing['pagador_norm']] ?? null) == $pago['cliente_id']);
        $shared = array_intersect(ytokens($ing['pagador']), $cliToks);
        $nameMatch = count(array_filter($shared, fn($t)=>($df[$t]??99)<=3)) >= 1;
        if ($amtMatch || $aliasMatch || $nameMatch) {
            $score = ($aliasMatch?100:0) + ($nameMatch?10:0) + ($amtMatch?5:0);
            $sug[] = ['yapeId'=>$ing['yape_id'],'fecha'=>$ing['fecha'],'pagador'=>$ing['pagador'],'pagadorNorm'=>$ing['pagador_norm'],'monto'=>(float)$ing['monto'],'detalle'=>$ing['detalle'],'score'=>$score,'amtMatch'=>$amtMatch,'aliasMatch'=>$aliasMatch,'nameMatch'=>$nameMatch];
        }
    }
    usort($sug, fn($a,$b)=>$b['score']-$a['score']);
    resp(true, ['sugerencias'=>array_slice($sug, 0, 25)], 'ok');
}

elseif ($action === 'crearVincular') {
    // Crea un pago NUEVO (ya Pagado) a partir de un ingreso Yape que no tenía cargo pendiente, y lo concilia.
    $in = inp(); $yapeId = $in['yapeId'] ?? ''; $cid = $in['clienteId'] ?? null; $concepto = trim($in['concepto'] ?? ''); $pn = $in['pagadorNorm'] ?? '';
    if (!$yapeId || !$cid) resp(false, null, 'Falta el cliente', 400);
    $r = $pdo->prepare("SELECT fecha, monto FROM yape_ingresos WHERE yape_id=?"); $r->execute([$yapeId]); $ir = $r->fetch();
    if (!$ir) resp(false, null, 'Ingreso no existe', 404);
    if ($concepto === '') $concepto = 'Pago Yape';
    $fecha = $ir['fecha']; $monto = (float)$ir['monto']; $idPago = 'YAPE-' . $yapeId;
    $data = json_encode(['id'=>$idPago,'clienteId'=>(int)$cid,'concepto'=>$concepto,'monto'=>$monto,'estado'=>'Pagado','metodoPago'=>'Yape','fecha'=>$fecha,'fechaVencimiento'=>$fecha,'refYape'=>$yapeId,'notas'=>'Creado desde conciliación Yape'], JSON_UNESCAPED_UNICODE);
    $pdo->beginTransaction();
    $pdo->prepare("INSERT INTO pagos (id,cliente_id,concepto,monto,estado,fecha,fecha_vencimiento,metodo_pago,ref_yape,data) VALUES (?,?,?,?,?,?,?,?,?,?)")
        ->execute([$idPago, $cid, $concepto, $monto, 'Pagado', $fecha, $fecha, 'Yape', $yapeId, $data]);
    $pk = (int)$pdo->lastInsertId();
    $pdo->prepare("UPDATE yape_ingresos SET estado='conciliado', pago_pk=? WHERE yape_id=?")->execute([$pk, $yapeId]);
    if ($pn) $pdo->prepare("INSERT INTO yape_aliases (pagador_norm,cliente_id) VALUES (?,?) ON DUPLICATE KEY UPDATE cliente_id=VALUES(cliente_id)")->execute([$pn, $cid]);
    $pdo->commit();
    resp(true, ['pk'=>$pk], 'Pago creado y conciliado');
}

else { resp(false, null, 'Acción inválida', 400); }
