-- cambios.sql · MySQL 8+ (InnoDB). Para BDs sin Artisan: ejecuta en orden y omite lo ya aplicado.
-- Ref. migraciones Laravel: 2026-05-01..07 actas; 2026-05-03 personeros (DML + columnas perfil); 2026-05-05 personeros.referencias;
--   2026-07-21 índices/pagos; 2026-07-22 users.phone; 2026-05-18 gestión de candidatos (managed_candidates); 2026-05-21 managed_candidates.import_reference;
--   Excel gestión candidatos 2026-05-25: list_scope_* (ámbito listado) + ubigeo personal department_id/province_id/district_id.
--   2026-09-07: zones por distrito + candidates.zone_id (bloque al final).

SET NAMES utf8mb4;

-- 2026-07-22 · users: quitar `phone` si existe (idempotente)
SET @drop_users_phone_sql = (
  SELECT IF(
    COUNT(*) > 0,
    'ALTER TABLE `users` DROP COLUMN `phone`',
    'SELECT ''omitido: users.phone no existe'' AS info'
  )
  FROM INFORMATION_SCHEMA.COLUMNS
  WHERE TABLE_SCHEMA = DATABASE()
    AND TABLE_NAME = 'users'
    AND COLUMN_NAME = 'phone'
);
PREPARE stmt FROM @drop_users_phone_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

SET FOREIGN_KEY_CHECKS = 0;

-- 2026-05-01 / 05-02 · tabla `actas` por mesa (no mezclar con esquema antiguo solo por local; si aplica, DROP previo comentado abajo)
-- DROP TABLE IF EXISTS `actas`;
CREATE TABLE `actas` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `voting_table_id` bigint unsigned NOT NULL,
  `reference` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `actas_voting_table_id_unique` (`voting_table_id`),
  CONSTRAINT `actas_voting_table_id_foreign`
    FOREIGN KEY (`voting_table_id`) REFERENCES `voting_tables` (`id`)
    ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

SET FOREIGN_KEY_CHECKS = 1;

-- 2026-05-04 · actas: estado
ALTER TABLE `actas`
  ADD COLUMN `status` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pending' AFTER `reference`;

-- 2026-05-06 · actas: notas + historial
ALTER TABLE `actas`
  ADD COLUMN `notes` text COLLATE utf8mb4_unicode_ci DEFAULT NULL AFTER `status`;

CREATE TABLE `acta_histories` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `acta_id` bigint unsigned NOT NULL,
  `from_status` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `to_status` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
  `notes` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `user_id` bigint unsigned DEFAULT NULL,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `acta_histories_acta_id_created_at_index` (`acta_id`,`created_at`),
  CONSTRAINT `acta_histories_acta_id_foreign`
    FOREIGN KEY (`acta_id`) REFERENCES `actas` (`id`) ON DELETE CASCADE,
  CONSTRAINT `acta_histories_user_id_foreign`
    FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 2026-05-07 · fotos de acta
CREATE TABLE `acta_photos` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `acta_id` bigint unsigned NOT NULL,
  `file_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `file_path` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `file_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `file_size` bigint unsigned DEFAULT NULL,
  `uploaded_by` bigint unsigned DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `acta_photos_acta_id_created_at_index` (`acta_id`,`created_at`),
  CONSTRAINT `acta_photos_acta_id_foreign`
    FOREIGN KEY (`acta_id`) REFERENCES `actas` (`id`) ON DELETE CASCADE,
  CONSTRAINT `acta_photos_uploaded_by_foreign`
    FOREIGN KEY (`uploaded_by`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 2026-05-03 · personeros: reasignar funciones suplente no local → simpatizante (solo datos)
UPDATE `personeros`
SET
  `function` = 'simpatizante',
  `voting_table_id` = NULL,
  `voting_location_id` = NULL,
  `district_id` = NULL,
  `province_id` = NULL,
  `department_id` = NULL,
  `updated_at` = NOW()
WHERE `deleted_at` IS NULL
  AND `function` IN (
    'coord_regional_suplente',
    'coord_provincial_suplente',
    'coord_distrital_suplente',
    'coord_politico_suplente'
  );

-- 2026-05-03 · personeros: DNI fotos, redes, perfil (columnas tras `phone`)
ALTER TABLE `personeros`
  ADD COLUMN `dni_photo_front_path` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL AFTER `phone`,
  ADD COLUMN `dni_photo_back_path` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL AFTER `dni_photo_front_path`,
  ADD COLUMN `facebook_url` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL AFTER `dni_photo_back_path`,
  ADD COLUMN `instagram_url` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL AFTER `facebook_url`,
  ADD COLUMN `tiktok_url` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL AFTER `instagram_url`,
  ADD COLUMN `profile_html` longtext COLLATE utf8mb4_unicode_ci DEFAULT NULL AFTER `tiktok_url`;



-- 2026-07-21 · personeros: índice único en `dni` → índice normal (soft delete). Si el nombre del UNIQUE difiere: SHOW INDEX FROM personeros
ALTER TABLE `personeros` DROP INDEX `personeros_dni_unique`;
ALTER TABLE `personeros` ADD INDEX `personeros_dni_index` (`dni`);

-- 2026-07-21 · voting_tables: mismo criterio (local + número mesa)
ALTER TABLE `voting_tables` DROP INDEX `voting_tables_voting_location_id_table_number_unique`;
ALTER TABLE `voting_tables` ADD INDEX `voting_tables_voting_location_id_table_number_index` (`voting_location_id`, `table_number`);

-- 2026-07-21 · pagos P1/P2 + payment_vouchers.payment_number (requiere expected_payment y payment_amount previos)
ALTER TABLE `personeros`
  ADD COLUMN `payment_1` decimal(10,2) DEFAULT NULL AFTER `expected_payment`,
  ADD COLUMN `payment_2` decimal(10,2) DEFAULT NULL AFTER `payment_1`;

UPDATE `personeros`
SET `payment_1` = `payment_amount`
WHERE `payment_amount` IS NOT NULL AND `payment_amount` > 0;

ALTER TABLE `payment_vouchers`
  ADD COLUMN `payment_number` tinyint unsigned DEFAULT NULL AFTER `personero_id`;

-- Opcional: registrar en `migrations` el mismo orden que artisan (batch a tu criterio).
-- Post fotos acta en web: `php artisan storage:link` en el servidor de la app.


-- 2026-05-05 · personeros: referencias (HTML enriquecido saneado, tras perfil)
ALTER TABLE `personeros`
  ADD COLUMN `referencias` text COLLATE utf8mb4_unicode_ci DEFAULT NULL AFTER `profile_html`;

-- =============================================================================
-- 2026-05-18 · Gestión de candidatos (migración 2026_05_18_100000_create_candidate_management_tables)
-- Requiere tablas: departments, provinces, districts, users
-- =============================================================================

SET FOREIGN_KEY_CHECKS = 0;

CREATE TABLE `candidate_positions` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `slug` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `description` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `sort_order` smallint unsigned NOT NULL DEFAULT 0,
  `is_active` tinyint(1) NOT NULL DEFAULT 1,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `candidate_positions_slug_unique` (`slug`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE `candidate_formulas` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `department_id` bigint unsigned DEFAULT NULL,
  `province_id` bigint unsigned DEFAULT NULL,
  `district_id` bigint unsigned DEFAULT NULL,
  `notes` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `is_active` tinyint(1) NOT NULL DEFAULT 1,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `candidate_formulas_department_id_foreign` (`department_id`),
  KEY `candidate_formulas_province_id_foreign` (`province_id`),
  KEY `candidate_formulas_district_id_foreign` (`district_id`),
  CONSTRAINT `candidate_formulas_department_id_foreign`
    FOREIGN KEY (`department_id`) REFERENCES `departments` (`id`) ON DELETE SET NULL,
  CONSTRAINT `candidate_formulas_province_id_foreign`
    FOREIGN KEY (`province_id`) REFERENCES `provinces` (`id`) ON DELETE SET NULL,
  CONSTRAINT `candidate_formulas_district_id_foreign`
    FOREIGN KEY (`district_id`) REFERENCES `districts` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE `candidate_field_definitions` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `label` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `slug` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `field_type` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
  `counts_for_progress` tinyint(1) NOT NULL DEFAULT 1,
  `track_status` tinyint(1) NOT NULL DEFAULT 1,
  `sort_order` smallint unsigned NOT NULL DEFAULT 0,
  `is_active` tinyint(1) NOT NULL DEFAULT 1,
  `help_text` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `options` json DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `candidate_field_definitions_slug_unique` (`slug`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE `managed_candidates` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `candidate_formula_id` bigint unsigned DEFAULT NULL,
  `candidate_position_id` bigint unsigned NOT NULL,
  `list_number` smallint unsigned DEFAULT NULL,
  `department_id` bigint unsigned DEFAULT NULL,
  `province_id` bigint unsigned DEFAULT NULL,
  `district_id` bigint unsigned DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  `deleted_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `managed_candidates_department_id_province_id_district_id_index` (`department_id`,`province_id`,`district_id`),
  KEY `managed_candidates_candidate_formula_id_index` (`candidate_formula_id`),
  KEY `managed_candidates_candidate_position_id_foreign` (`candidate_position_id`),
  KEY `managed_candidates_department_id_foreign` (`department_id`),
  KEY `managed_candidates_province_id_foreign` (`province_id`),
  KEY `managed_candidates_district_id_foreign` (`district_id`),
  CONSTRAINT `managed_candidates_candidate_formula_id_foreign`
    FOREIGN KEY (`candidate_formula_id`) REFERENCES `candidate_formulas` (`id`) ON DELETE SET NULL,
  CONSTRAINT `managed_candidates_candidate_position_id_foreign`
    FOREIGN KEY (`candidate_position_id`) REFERENCES `candidate_positions` (`id`) ON DELETE CASCADE,
  CONSTRAINT `managed_candidates_department_id_foreign`
    FOREIGN KEY (`department_id`) REFERENCES `departments` (`id`) ON DELETE SET NULL,
  CONSTRAINT `managed_candidates_province_id_foreign`
    FOREIGN KEY (`province_id`) REFERENCES `provinces` (`id`) ON DELETE SET NULL,
  CONSTRAINT `managed_candidates_district_id_foreign`
    FOREIGN KEY (`district_id`) REFERENCES `districts` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE `managed_candidate_field_values` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `managed_candidate_id` bigint unsigned NOT NULL,
  `candidate_field_definition_id` bigint unsigned NOT NULL,
  `value_text` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `file_path` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `status` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'falta',
  `observations` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `updated_by` bigint unsigned DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `mc_field_unique` (`managed_candidate_id`,`candidate_field_definition_id`),
  KEY `managed_candidate_field_values_status_index` (`status`),
  KEY `mcfv_managed_candidate_fk` (`managed_candidate_id`),
  KEY `mcfv_field_definition_fk` (`candidate_field_definition_id`),
  KEY `managed_candidate_field_values_updated_by_foreign` (`updated_by`),
  CONSTRAINT `mcfv_managed_candidate_fk`
    FOREIGN KEY (`managed_candidate_id`) REFERENCES `managed_candidates` (`id`) ON DELETE CASCADE,
  CONSTRAINT `mcfv_field_definition_fk`
    FOREIGN KEY (`candidate_field_definition_id`) REFERENCES `candidate_field_definitions` (`id`) ON DELETE CASCADE,
  CONSTRAINT `managed_candidate_field_values_updated_by_foreign`
    FOREIGN KEY (`updated_by`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

SET FOREIGN_KEY_CHECKS = 1;

-- 2026-05-18 · Datos iniciales (opcional; equivale a CandidateManagementSeeder; idempotente por slug)
INSERT INTO `candidate_positions` (`name`, `slug`, `sort_order`, `is_active`, `created_at`, `updated_at`) VALUES
  ('Alcalde', 'alcalde', 1, 1, NOW(), NOW()),
  ('Vicealcalde', 'vicealcalde', 2, 1, NOW(), NOW()),
  ('Regidor', 'regidor', 3, 1, NOW(), NOW())
ON DUPLICATE KEY UPDATE
  `name` = VALUES(`name`),
  `sort_order` = VALUES(`sort_order`),
  `is_active` = VALUES(`is_active`),
  `updated_at` = VALUES(`updated_at`);

INSERT INTO `candidate_field_definitions` (
  `label`, `slug`, `field_type`, `counts_for_progress`, `track_status`, `sort_order`, `is_active`, `options`, `created_at`, `updated_at`
) VALUES
  ('Nombres y Apellidos', 'nombres_apellidos', 'text', 1, 1, 1, 1, NULL, NOW(), NOW()),
  ('DNI', 'dni', 'text', 1, 1, 2, 1, NULL, NOW(), NOW()),
  ('Foto DNI', 'foto_dni', 'file', 1, 1, 3, 1, JSON_OBJECT('accept', 'image/*,application/pdf'), NOW(), NOW()),
  ('CV', 'cv', 'file', 1, 1, 4, 1, JSON_OBJECT('accept', 'application/pdf'), NOW(), NOW()),
  ('Patrimonio', 'patrimonio', 'file', 1, 1, 5, 1, JSON_OBJECT('accept', 'application/pdf'), NOW(), NOW()),
  ('Antecedentes', 'antecedentes', 'file', 1, 1, 6, 1, JSON_OBJECT('accept', 'application/pdf'), NOW(), NOW())
ON DUPLICATE KEY UPDATE
  `label` = VALUES(`label`),
  `field_type` = VALUES(`field_type`),
  `counts_for_progress` = VALUES(`counts_for_progress`),
  `track_status` = VALUES(`track_status`),
  `sort_order` = VALUES(`sort_order`),
  `is_active` = VALUES(`is_active`),
  `options` = VALUES(`options`),
  `updated_at` = VALUES(`updated_at`);

-- 2026_05_22 · «Filtra» por candidato: filter_yes en managed_candidate_field_values; quitar use_in_filter de candidate_field_definitions.
ALTER TABLE `managed_candidate_field_values`
  ADD COLUMN `filter_yes` tinyint(1) NOT NULL DEFAULT 1 AFTER `status`;

UPDATE `managed_candidate_field_values` AS `v`
INNER JOIN `candidate_field_definitions` AS `d` ON `d`.`id` = `v`.`candidate_field_definition_id`
SET `v`.`filter_yes` = `d`.`use_in_filter`;

ALTER TABLE `candidate_field_definitions` DROP COLUMN `use_in_filter`;

-- 2026-05-20 · Tipo de campo «date» (solo aplicación; mismo VARCHAR(32) en `field_type`)
-- Valor guardado en `managed_candidate_field_values.value_text`: fecha ISO YYYY-MM-DD.
-- JSON `options` para `field_type` = 'date':
--   date_list_as: 'date' | 'age'  (listado web: fecha o edad; en formulario, edad bajo el campo si «age»; Excel siempre ISO)

-- Opcional: registrar migración en Laravel
-- INSERT INTO `migrations` (`migration`, `batch`) VALUES ('2026_05_18_100000_create_candidate_management_tables', 1);
-- INSERT INTO `migrations` (`migration`, `batch`) VALUES ('2026_05_19_100000_add_use_in_filter_to_candidate_field_definitions', 1);
-- INSERT INTO `migrations` (`migration`, `batch`) VALUES ('2026_05_20_120000_candidate_field_date_type', 1);

-- 2026-05-21 · managed_candidates.import_reference (código importación Excel; único; nullable)
ALTER TABLE `managed_candidates` ADD COLUMN `import_reference` varchar(80) NULL AFTER `id`, ADD UNIQUE KEY `managed_candidates_import_reference_unique` (`import_reference`);

-- 2026-05-25 · managed_candidates: ámbito del listado (independiente del ubigeo personal)
ALTER TABLE `managed_candidates`
  ADD COLUMN `list_scope_department_id` bigint unsigned DEFAULT NULL AFTER `district_id`,
  ADD COLUMN `list_scope_province_id` bigint unsigned DEFAULT NULL AFTER `list_scope_department_id`,
  ADD COLUMN `list_scope_district_id` bigint unsigned DEFAULT NULL AFTER `list_scope_province_id`,
  ADD KEY `managed_candidates_list_scope_ubigeo_index` (`list_scope_department_id`,`list_scope_province_id`,`list_scope_district_id`),
  ADD CONSTRAINT `managed_candidates_list_scope_department_id_foreign`
    FOREIGN KEY (`list_scope_department_id`) REFERENCES `departments` (`id`) ON DELETE SET NULL,
  ADD CONSTRAINT `managed_candidates_list_scope_province_id_foreign`
    FOREIGN KEY (`list_scope_province_id`) REFERENCES `provinces` (`id`) ON DELETE SET NULL,
  ADD CONSTRAINT `managed_candidates_list_scope_district_id_foreign`
    FOREIGN KEY (`list_scope_district_id`) REFERENCES `districts` (`id`) ON DELETE SET NULL;

UPDATE `managed_candidates`
SET
  `list_scope_department_id` = `department_id`,
  `list_scope_province_id` = `province_id`,
  `list_scope_district_id` = `district_id`
WHERE `list_scope_department_id` IS NULL;

-- Ref. migración Laravel: 2026_05_25_180000_add_list_scope_ubigeo_to_managed_candidates_table.php
-- down(): quita índice `managed_candidates_list_scope_ubigeo_index` y columnas `list_scope_*` (FK incluidas) en orden inverso al up.
-- Opcional: registrar migración en Laravel (ajusta `batch` al siguiente libre en tu tabla `migrations`):
-- INSERT INTO `migrations` (`migration`, `batch`) VALUES ('2026_05_25_180000_add_list_scope_ubigeo_to_managed_candidates_table', 1);

-- 2026-05-24 · Puesto opcional: `managed_candidates.candidate_position_id` NULL; FK ON DELETE SET NULL
-- Equivale a: database/migrations/2026_05_24_200000_make_managed_candidates_candidate_position_id_nullable.php
ALTER TABLE `managed_candidates`
  DROP FOREIGN KEY `managed_candidates_candidate_position_id_foreign`;

ALTER TABLE `managed_candidates`
  MODIFY `candidate_position_id` bigint unsigned NULL;

ALTER TABLE `managed_candidates`
  ADD CONSTRAINT `managed_candidates_candidate_position_id_foreign`
    FOREIGN KEY (`candidate_position_id`) REFERENCES `candidate_positions` (`id`) ON DELETE SET NULL;

-- 2026-05-26 · Rellenar `import_reference` numérico (= CAST(id AS CHAR)) donde sea NULL.
-- Equivale a la migración Laravel `2026_05_26_100000_backfill_import_reference_on_managed_candidates.php`.
-- UPDATE `managed_candidates` SET `import_reference` = CAST(`id` AS CHAR) WHERE `import_reference` IS NULL;

-- 2026-06-04 · Columna `source` en `personeros`: origen del registro
-- Equivale a: database/migrations/2026_07_22_000002_add_source_to_personeros_table.php
-- Valores: 'manual' (panel admin / registro público) | 'external' (API de integración)
ALTER TABLE `personeros`
  ADD COLUMN `source` varchar(20) NOT NULL DEFAULT 'manual' AFTER `referencias`;

-- Útil para filtrar en el panel por origen:
-- SELECT * FROM personeros WHERE source = 'external';
-- SELECT * FROM personeros WHERE source = 'manual';
-- SELECT source, COUNT(*) AS total FROM personeros GROUP BY source;

-- 2026-06-05 · Tabla `tutorials`: archivos adjuntos de ayuda/manual por rol
-- Equivale a: database/migrations/2026_06_05_100000_create_tutorials_table.php
-- `target_roles` JSON nullable: null = todos los roles autenticados; ["admin","coordinator"] = solo esos roles.
CREATE TABLE `tutorials` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `description` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `file_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `file_path` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `file_type` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
  `file_size` bigint unsigned NOT NULL DEFAULT 0,
  `target_roles` json DEFAULT NULL,
  `is_active` tinyint(1) NOT NULL DEFAULT 1,
  `uploaded_by` bigint unsigned DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `tutorials_uploaded_by_foreign` (`uploaded_by`),
  CONSTRAINT `tutorials_uploaded_by_foreign`
    FOREIGN KEY (`uploaded_by`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- =============================================================================
-- 2026-06-29 / 06-30 · CANDIDATOS REGIONALES + BIBLIOTECA DE IMÁGENES + COLABORADOR + ENCUESTAS
-- Ref. migraciones Laravel: 2026_06_29_000001..000005 y 2026_06_30_000001
-- Requiere tablas previas: candidates, departments, provinces, districts, users,
--   personeros, political_parties, election_events, vote_tallies, tally_summaries.
-- Ejecutar en el orden indicado (omite lo ya aplicado). MySQL 8+ / InnoDB.
-- =============================================================================

-- 2026-06-29 · candidates: geografía regional (province_id, district_id) + `type` ENUM→VARCHAR(40)
-- Equivale a: 2026_06_29_000001_add_regional_types_and_geo_to_candidates.php
-- Habilita cargos regionales (regional_governor, regional_councilor, provincial_mayor, district_mayor);
-- los valores válidos se controlan en la app (App\Support\CandidateTypes).
ALTER TABLE `candidates`
  ADD COLUMN `province_id` bigint unsigned NULL AFTER `department_id`,
  ADD COLUMN `district_id` bigint unsigned NULL AFTER `province_id`,
  ADD KEY `candidates_province_id_foreign` (`province_id`),
  ADD KEY `candidates_district_id_foreign` (`district_id`),
  ADD CONSTRAINT `candidates_province_id_foreign` FOREIGN KEY (`province_id`) REFERENCES `provinces` (`id`) ON DELETE SET NULL,
  ADD CONSTRAINT `candidates_district_id_foreign` FOREIGN KEY (`district_id`) REFERENCES `districts` (`id`) ON DELETE SET NULL,
  MODIFY COLUMN `type` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL;

-- 2026-06-29 · vote_tallies / tally_summaries: `candidate_type` ENUM→VARCHAR(40) (admite cargos regionales)
-- Equivale a: 2026_06_29_000002_extend_candidate_type_in_vote_tables.php
ALTER TABLE `vote_tallies`    MODIFY COLUMN `candidate_type` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL;
ALTER TABLE `tally_summaries` MODIFY COLUMN `candidate_type` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL;

-- 2026-07-24 · tally_attachments: `candidate_type` ENUM→VARCHAR(40) (faltaba; rompía evidencias de cargos regionales)
-- Equivale a: 2026_07_24_000001_extend_candidate_type_in_tally_attachments.php
ALTER TABLE `tally_attachments` MODIFY COLUMN `candidate_type` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL;

-- 2026-06-29 · candidate_images: biblioteca de imágenes (carga masiva con código; se relaciona al importar candidatos)
-- Equivale a: 2026_06_29_000003_create_candidate_images_table.php
CREATE TABLE `candidate_images` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `code` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `path` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `candidate_images_code_unique` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 2026-06-29 · personeros.is_collaborator (persona que apoya al partido; se lista aparte de personeros)
-- Equivale a: 2026_06_29_000004_add_is_collaborator_to_personeros.php
ALTER TABLE `personeros`
  ADD COLUMN `is_collaborator` tinyint(1) NOT NULL DEFAULT 0 AFTER `function`,
  ADD KEY `personeros_is_collaborator_index` (`is_collaborator`);

-- 2026-06-29 · Encuestas: surveys + cargos objetivo + respuestas + respuestas por cargo
-- Equivale a: 2026_06_29_000005_create_surveys_tables.php
CREATE TABLE `surveys` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `description` text COLLATE utf8mb4_unicode_ci,
  `status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'draft',
  `allow_anonymous` tinyint(1) NOT NULL DEFAULT 1,
  `allow_identified` tinyint(1) NOT NULL DEFAULT 1,
  `is_public` tinyint(1) NOT NULL DEFAULT 0,
  `public_slug` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `created_by` bigint unsigned DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `surveys_public_slug_unique` (`public_slug`),
  KEY `surveys_created_by_foreign` (`created_by`),
  CONSTRAINT `surveys_created_by_foreign` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE `survey_target_positions` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `survey_id` bigint unsigned NOT NULL,
  `candidate_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `sort_order` smallint unsigned NOT NULL DEFAULT 0,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `survey_target_positions_survey_id_candidate_type_unique` (`survey_id`,`candidate_type`),
  CONSTRAINT `survey_target_positions_survey_id_foreign` FOREIGN KEY (`survey_id`) REFERENCES `surveys` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE `survey_responses` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `survey_id` bigint unsigned NOT NULL,
  `is_anonymous` tinyint(1) NOT NULL DEFAULT 0,
  `respondent_dni` varchar(12) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `respondent_full_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `department_id` bigint unsigned DEFAULT NULL,
  `province_id` bigint unsigned DEFAULT NULL,
  `district_id` bigint unsigned DEFAULT NULL,
  `source` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'public',
  `user_id` bigint unsigned DEFAULT NULL,
  `personero_id` bigint unsigned DEFAULT NULL,
  `ip` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `survey_responses_survey_id_source_index` (`survey_id`,`source`),
  KEY `survey_responses_department_id_foreign` (`department_id`),
  KEY `survey_responses_province_id_foreign` (`province_id`),
  KEY `survey_responses_district_id_foreign` (`district_id`),
  KEY `survey_responses_user_id_foreign` (`user_id`),
  KEY `survey_responses_personero_id_foreign` (`personero_id`),
  CONSTRAINT `survey_responses_survey_id_foreign` FOREIGN KEY (`survey_id`) REFERENCES `surveys` (`id`) ON DELETE CASCADE,
  CONSTRAINT `survey_responses_department_id_foreign` FOREIGN KEY (`department_id`) REFERENCES `departments` (`id`) ON DELETE SET NULL,
  CONSTRAINT `survey_responses_province_id_foreign` FOREIGN KEY (`province_id`) REFERENCES `provinces` (`id`) ON DELETE SET NULL,
  CONSTRAINT `survey_responses_district_id_foreign` FOREIGN KEY (`district_id`) REFERENCES `districts` (`id`) ON DELETE SET NULL,
  CONSTRAINT `survey_responses_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL,
  CONSTRAINT `survey_responses_personero_id_foreign` FOREIGN KEY (`personero_id`) REFERENCES `personeros` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE `survey_response_answers` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `survey_response_id` bigint unsigned NOT NULL,
  `candidate_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `candidate_id` bigint unsigned DEFAULT NULL,
  `political_party_id` bigint unsigned DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `survey_response_answers_survey_response_id_candidate_type_unique` (`survey_response_id`,`candidate_type`),
  KEY `survey_response_answers_candidate_type_candidate_id_index` (`candidate_type`,`candidate_id`),
  KEY `survey_response_answers_candidate_id_foreign` (`candidate_id`),
  KEY `survey_response_answers_political_party_id_foreign` (`political_party_id`),
  CONSTRAINT `survey_response_answers_survey_response_id_foreign` FOREIGN KEY (`survey_response_id`) REFERENCES `survey_responses` (`id`) ON DELETE CASCADE,
  CONSTRAINT `survey_response_answers_candidate_id_foreign` FOREIGN KEY (`candidate_id`) REFERENCES `candidates` (`id`) ON DELETE SET NULL,
  CONSTRAINT `survey_response_answers_political_party_id_foreign` FOREIGN KEY (`political_party_id`) REFERENCES `political_parties` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 2026-06-30 · surveys: evento electoral (obligatorio en la app) + alcance
-- Equivale a: 2026_06_30_000001_add_event_and_scope_to_surveys.php
-- election_scope: 'national' (Elecciones Generales) | 'regional' (ERM) | 'both' (todos)
ALTER TABLE `surveys`
  ADD COLUMN `election_event_id` bigint unsigned NULL AFTER `id`,
  ADD COLUMN `election_scope` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'both' AFTER `description`,
  ADD KEY `surveys_election_event_id_foreign` (`election_event_id`),
  ADD CONSTRAINT `surveys_election_event_id_foreign` FOREIGN KEY (`election_event_id`) REFERENCES `election_events` (`id`) ON DELETE SET NULL;

-- Opcional: registrar las migraciones en Laravel (ajusta `batch` al siguiente libre en tu tabla `migrations`):
-- INSERT INTO `migrations` (`migration`, `batch`) VALUES
--   ('2026_06_29_000001_add_regional_types_and_geo_to_candidates', 1),
--   ('2026_06_29_000002_extend_candidate_type_in_vote_tables', 1),
--   ('2026_06_29_000003_create_candidate_images_table', 1),
--   ('2026_06_29_000004_add_is_collaborator_to_personeros', 1),
--   ('2026_06_29_000005_create_surveys_tables', 1),
--   ('2026_06_30_000001_add_event_and_scope_to_surveys', 1),
--   ('2026_07_24_000001_extend_candidate_type_in_tally_attachments', 1);

-- Post-deploy (app): `php artisan storage:link` (biblioteca de imágenes y fotos de candidatos bajo /storage).
-- Post-deploy (permisos): `storage/app/public` debe ser ESCRIBIBLE por el usuario del servidor web
--   (p. ej. `chown -R www-data:www-data storage && chmod -R u+rwX storage`). Si un subdirectorio
--   (candidate-images, candidates, parties…) queda como root sin escritura, las subidas fallan en
--   silencio y la ruta se guarda vacía → la imagen no se muestra.

-- ─────────────────────────────────────────────────────────────────────────────
-- 2026-07-16 · Registro público 3 tipos + import masivo de imágenes (SIN cambios de esquema)
-- Post-deploy (infra de subidas — replicar en producción):
--   nginx:  client_max_body_size 128M;  (bloque server)
--   PHP:    upload_max_filesize=12M, post_max_size=120M, max_file_uploads=120
--           (en Docker: docker/php/uploads.ini montado como zz-uploads.ini)
-- Post-deploy (env): el registro público ya NO exige OTP de WhatsApp por defecto.
--   Para reactivarlo cuando exista la UI: PUBLIC_REGISTRATION_OTP=true en .env.

-- =============================================================================
-- =============================================================================
-- PENDIENTE DE EJECUTAR EN PRODUCCION  (al 2026-09-09)
-- =============================================================================
-- Todo lo que sigue a partir de aqui esta SIN aplicar en produccion. Se puede
-- ejecutar de una sola vez, en este orden, y las veces que haga falta: cada
-- bloque comprueba antes si el objeto ya existe y se omite si es asi.
--
--   1. Zonas por distrito            -> crea `zones` y `candidates.zone_id`
--   2. Zona del encuestado           -> anade `survey_responses.zone_id`
--   3. Ambito del evento electoral   -> anade `election_events.election_scope`
--   4. Permisos por perfil y accion  -> crea `role_permissions` (70 filas)
--   5. Estado del candidato          -> anade `candidates.status`
--   6. Candidatos de partidos ya creados -> 1 por cargo regional/municipal (DATOS)
--   7. Participacion desmarcada      -> los candidatos sin nombre a "No participa" (DATOS)
--   8. Indices por sitio del candidato -> `candidates` (cargo + partido + lugar)
--
-- Si ya ejecutaste los bloques 1 a 6, ejecuta solo el 7 y el 8 (estan al final).
--
-- Comprobado sobre una copia de la base de produccion: ejecutado dos veces
-- seguidas deja exactamente el mismo resultado y la aplicacion responde bien.
--
-- Antes: respaldar la base. Despues: `php artisan config:clear`.
--
-- Para comprobar que quedo todo:
--   SELECT 'zones', COUNT(*) FROM information_schema.tables
--     WHERE table_schema=DATABASE() AND table_name='zones'
--   UNION ALL SELECT 'candidates.zone_id', COUNT(*) FROM information_schema.columns
--     WHERE table_schema=DATABASE() AND table_name='candidates' AND column_name='zone_id'
--   UNION ALL SELECT 'survey_responses.zone_id', COUNT(*) FROM information_schema.columns
--     WHERE table_schema=DATABASE() AND table_name='survey_responses' AND column_name='zone_id'
--   UNION ALL SELECT 'election_events.election_scope', COUNT(*) FROM information_schema.columns
--     WHERE table_schema=DATABASE() AND table_name='election_events' AND column_name='election_scope'
--   UNION ALL SELECT 'role_permissions', COUNT(*) FROM information_schema.tables
--     WHERE table_schema=DATABASE() AND table_name='role_permissions';
--   -- Los cinco deben devolver 1.
--   SELECT COUNT(*) FROM `role_permissions`;  -- debe devolver 70
--
-- Los eventos que ya existen quedan con ambito 'both' (ven todos los cargos) y
-- nadie gana ni pierde accesos: los permisos sembrados reproducen lo que cada
-- perfil ya podia hacer.
-- =============================================================================


-- 2026-09-07 · ZONAS POR DISTRITO + ZONA DEL CANDIDATO
-- Equivale a: 2026_09_07_000001_create_zones_and_add_zone_to_candidates.php
-- Requiere: districts y candidates, incluida la columna candidates.district_id.
-- Este bloque es una alternativa a ejecutar esa migración con Artisan.
-- Si los cambios anteriores ya están aplicados, ejecutar SOLO este bloque.
-- Los candidatos existentes mantienen zone_id = NULL; asignarles su zona desde
-- el formulario o la importación. No se crean zonas ni se reasignan datos automáticamente.
-- =============================================================================

CREATE TABLE IF NOT EXISTS `zones` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `district_id` bigint unsigned NOT NULL,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `zones_district_id_name_unique` (`district_id`, `name`),
  CONSTRAINT `zones_district_id_foreign`
    FOREIGN KEY (`district_id`) REFERENCES `districts` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- Omitir la columna si ya fue creada por este bloque o por la migración Laravel.
SET @add_candidate_zone_column_sql = (
  SELECT IF(
    COUNT(*) = 0,
    'ALTER TABLE `candidates` ADD COLUMN `zone_id` bigint unsigned NULL AFTER `district_id`',
    'SELECT ''omitido: candidates.zone_id ya existe'' AS info'
  )
  FROM INFORMATION_SCHEMA.COLUMNS
  WHERE TABLE_SCHEMA = DATABASE()
    AND TABLE_NAME = 'candidates'
    AND COLUMN_NAME = 'zone_id'
);
PREPARE stmt FROM @add_candidate_zone_column_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

SET @add_candidate_zone_fk_sql = (
  SELECT IF(
    COUNT(*) = 0,
    'ALTER TABLE `candidates` ADD CONSTRAINT `candidates_zone_id_foreign` FOREIGN KEY (`zone_id`) REFERENCES `zones` (`id`) ON DELETE SET NULL',
    'SELECT ''omitido: candidates_zone_id_foreign ya existe'' AS info'
  )
  FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
  WHERE CONSTRAINT_SCHEMA = DATABASE()
    AND TABLE_NAME = 'candidates'
    AND CONSTRAINT_NAME = 'candidates_zone_id_foreign'
    AND CONSTRAINT_TYPE = 'FOREIGN KEY'
);
PREPARE stmt FROM @add_candidate_zone_fk_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

-- Si se aplica este bloque manualmente y luego se usará Artisan, registrar la
-- migración SOLO después de confirmar que el bloque finalizó correctamente.
-- El INSERT está comentado para no marcar una migración si hubo errores previos.
-- INSERT INTO `migrations` (`migration`, `batch`)
-- SELECT '2026_09_07_000001_create_zones_and_add_zone_to_candidates',
--        COALESCE((SELECT MAX(`batch`) FROM `migrations`), 0) + 1
-- WHERE NOT EXISTS (
--   SELECT 1 FROM `migrations`
--   WHERE `migration` = '2026_09_07_000001_create_zones_and_add_zone_to_candidates'
-- );

-- Mapeo de los demás cambios de esta entrega (SIN cambios adicionales de esquema):
-- · Numero usa candidates.number, que ya existía; Tipo usa candidates.type.
-- · La plantilla permite mezclar tipos; Zona se resuelve dentro de su distrito.
-- · La obligatoriedad de Zona para Alcalde Distrital y la pertenencia al distrito
--   se validan en la aplicación; las FK anteriores garantizan la existencia.
-- · Logo: se usa political_parties.logo. No copiarlo a candidates.photo ni borrar
--   fotos antiguas; el control de errores de almacenamiento se realiza en PHP.
-- · Cédula responsive, permisos, cierre de rutas y consulta de DNI son cambios
--   de código/configuración. No necesitan ALTER TABLE ni claves API dentro del SQL.

-- 2026-09-07 · Importación Excel de Zonas (SIN cambios adicionales de esquema):
-- · Ubigeos > Zonas: descargar plantilla e importar Departamento, Provincia,
--   Distrito y Zona. Se usa la tabla zones creada por el bloque anterior.
-- · Repetidas en el mismo distrito se omiten; errores por fila descargables.
-- · No ejecutar otra vez todo este histórico para habilitar la importación.
-- · Contrato, límites y despliegue: docs/importacion-zonas.md.

-- =============================================================================
-- 2026-09-07 · CORRECCIÓN: ZONA SOLO EN ENCUESTAS, NO EN CANDIDATOS
-- Sustituye la regla funcional de zona obligatoria para Alcalde Distrital indicada
-- en el bloque anterior. El formulario, plantilla y exportación de candidatos
-- ahora terminan en Distrito; una columna Zona de un Excel antiguo se ignora.
-- Equivale a: 2026_09_07_000002_add_zone_to_survey_responses.php
-- Requiere zones y survey_responses. Ejecutar SOLO este bloque si lo anterior ya
-- está aplicado. Alternativa a la migración Artisan; no ejecutar ambos a ciegas.
-- No se borra candidates.zone_id ni se trasladan sus valores: NO representan la
-- ubicación del encuestado. Respuestas existentes quedan con zone_id = NULL.
-- Aplicar ANTES de desplegar el código que registra/filtra zonas en encuestas.
-- =============================================================================

SET @add_survey_zone_column_sql = (
  SELECT IF(
    COUNT(*) = 0,
    'ALTER TABLE `survey_responses` ADD COLUMN `zone_id` bigint unsigned NULL AFTER `district_id`',
    'SELECT ''omitido: survey_responses.zone_id ya existe'' AS info'
  )
  FROM INFORMATION_SCHEMA.COLUMNS
  WHERE TABLE_SCHEMA = DATABASE()
    AND TABLE_NAME = 'survey_responses'
    AND COLUMN_NAME = 'zone_id'
);
PREPARE stmt FROM @add_survey_zone_column_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

SET @add_survey_zone_fk_sql = (
  SELECT IF(
    COUNT(*) = 0,
    'ALTER TABLE `survey_responses` ADD CONSTRAINT `survey_responses_zone_id_foreign` FOREIGN KEY (`zone_id`) REFERENCES `zones` (`id`) ON DELETE SET NULL',
    'SELECT ''omitido: survey_responses_zone_id_foreign ya existe'' AS info'
  )
  FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
  WHERE CONSTRAINT_SCHEMA = DATABASE()
    AND TABLE_NAME = 'survey_responses'
    AND CONSTRAINT_NAME = 'survey_responses_zone_id_foreign'
    AND CONSTRAINT_TYPE = 'FOREIGN KEY'
);
PREPARE stmt FROM @add_survey_zone_fk_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

-- Tras ejecutar y verificar el bloque manual, opcionalmente registrar la migración
-- como aplicada para evitar que Artisan intente agregar otra vez la columna:
-- INSERT INTO `migrations` (`migration`, `batch`)
-- SELECT '2026_09_07_000002_add_zone_to_survey_responses', COALESCE(MAX(`batch`), 0) + 1
-- FROM `migrations`
-- HAVING NOT EXISTS (
--   SELECT 1 FROM `migrations`
--   WHERE `migration` = '2026_09_07_000002_add_zone_to_survey_responses'
-- );
-- Zona es opcional; si se indica debe pertenecer al distrito del encuestado.
-- Resultados, participantes y sus exportaciones filtran por survey_responses.zone_id.
-- Los candidatos de la cédula siguen seleccionándose por cargo y ubigeo, NO por zona.
-- Consulta de DNI: MBL es el único proveedor, exclusivo de encuestas.
-- No guardar tokens ni respuestas personales en SQL.

-- ---------------------------------------------------------------------------
-- 2026-09-09: ámbito del evento electoral (election_events.election_scope)
-- ---------------------------------------------------------------------------
-- Distingue los dos procesos electorales peruanos para no ofrecer cargos que ese
-- proceso no elige: 'national' (Elecciones Generales: Presidencial, Senadores
-- Nacionales y Regionales, Diputados, Parlamento Andino), 'regional' (Elecciones
-- Regionales y Municipales: Gobernador Regional, Consejero Regional, Alcalde
-- Provincial, Alcalde Distrital) o 'both'.
-- Los eventos ya registrados quedan en 'both': siguen viendo todos los cargos.
-- Bloque idempotente: puede ejecutarse más de una vez sin efecto.
SET @add_event_scope_sql = (
  SELECT IF(
    COUNT(*) = 0,
    'ALTER TABLE `election_events` ADD COLUMN `election_scope` varchar(20) NOT NULL DEFAULT ''both'' AFTER `name`',
    'SELECT ''omitido: election_events.election_scope ya existe'' AS info'
  )
  FROM INFORMATION_SCHEMA.COLUMNS
  WHERE TABLE_SCHEMA = DATABASE()
    AND TABLE_NAME = 'election_events'
    AND COLUMN_NAME = 'election_scope'
);
PREPARE stmt FROM @add_event_scope_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

-- Tras ejecutar y verificar, opcionalmente registrar la migración como aplicada
-- para que Artisan no intente agregar otra vez la columna:
-- INSERT INTO `migrations` (`migration`, `batch`)
-- SELECT '2026_09_09_000001_add_election_scope_to_election_events', COALESCE(MAX(`batch`), 0) + 1
-- FROM `migrations`
-- HAVING NOT EXISTS (
--   SELECT 1 FROM `migrations`
--   WHERE `migration` = '2026_09_09_000001_add_election_scope_to_election_events'
-- );
-- El ámbito solo filtra lo que ofrecen conteo, resultados y actas; no borra ni
-- oculta votos ya registrados. Corregir el ámbito de un evento es seguro.


-- ---------------------------------------------------------------------------
-- 2026-09-09: permisos por perfil y accion (role_permissions)
-- ---------------------------------------------------------------------------
-- Hasta ahora los modulos solo escondian botones: el backend no los exigia y la
-- autorizacion real la daban middlewares de rol escritos en el codigo. Esta tabla
-- la vuelve un dato, editable desde "Roles y permisos".
--
-- Perfil = rol del usuario ('coordinator') o, para personeros, su funcion
-- ('personero:coord_distrital'). Acciones = lista separada por comas entre
-- view, create, update, delete, import y export.
-- Sin fila = sin acceso al modulo. Cualquier accion concedida implica poder ver.
-- 'admin' NO se guarda: tiene acceso total por codigo para que nadie pueda dejar
-- al sistema sin quien lo administre.
--
-- Lo sembrado reproduce lo que cada perfil ya podia hacer: un personero de mesa
-- registra votos (todas las acciones en conteo) pero solo consulta mesas y
-- eventos, que es lo que el filtro por rol le permitia antes.
-- Bloque idempotente: puede ejecutarse mas de una vez sin efecto.
CREATE TABLE IF NOT EXISTS `role_permissions` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `profile` varchar(64) NOT NULL,
  `module` varchar(48) NOT NULL,
  `actions` varchar(120) NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `role_permissions_profile_module_unique` (`profile`,`module`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- INSERT IGNORE + la clave unica (profile, module) hacen que re-ejecutar no
-- duplique filas ni pise lo que un administrador haya ajustado despues.
INSERT IGNORE INTO `role_permissions` (`profile`, `module`, `actions`, `created_at`, `updated_at`)
VALUES
('coordinator', 'parties', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('coordinator', 'candidates', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('coordinator', 'candidate-management', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('coordinator', 'surveys', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('coordinator', 'voting-locations', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('coordinator', 'voting-tables', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('coordinator', 'personeros', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('coordinator', 'ubigeos', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('coordinator', 'election-events', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('coordinator', 'vote-counting', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:personero', 'vote-counting', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:personero', 'surveys', 'view', NOW(), NOW()),
  ('personero:personero', 'voting-tables', 'view', NOW(), NOW()),
  ('personero:personero', 'election-events', 'view', NOW(), NOW()),
  ('personero:coord_local', 'vote-counting', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_local', 'surveys', 'view', NOW(), NOW()),
  ('personero:coord_local', 'voting-tables', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_local', 'election-events', 'view', NOW(), NOW()),
  ('personero:coord_local', 'voting-locations', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_local', 'personeros', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_local_suplente', 'vote-counting', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_local_suplente', 'surveys', 'view', NOW(), NOW()),
  ('personero:coord_local_suplente', 'voting-tables', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_local_suplente', 'election-events', 'view', NOW(), NOW()),
  ('personero:coord_local_suplente', 'voting-locations', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_local_suplente', 'personeros', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_distrital', 'vote-counting', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_distrital', 'surveys', 'view', NOW(), NOW()),
  ('personero:coord_distrital', 'voting-tables', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_distrital', 'election-events', 'view', NOW(), NOW()),
  ('personero:coord_distrital', 'voting-locations', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_distrital', 'personeros', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_distrital', 'candidate-management', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_provincial', 'vote-counting', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_provincial', 'surveys', 'view', NOW(), NOW()),
  ('personero:coord_provincial', 'voting-tables', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_provincial', 'election-events', 'view', NOW(), NOW()),
  ('personero:coord_provincial', 'voting-locations', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_provincial', 'personeros', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_provincial', 'candidate-management', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_regional', 'vote-counting', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_regional', 'surveys', 'view', NOW(), NOW()),
  ('personero:coord_regional', 'voting-tables', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_regional', 'election-events', 'view', NOW(), NOW()),
  ('personero:coord_regional', 'voting-locations', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_regional', 'personeros', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_regional', 'candidate-management', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:direccion_general', 'vote-counting', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:direccion_general', 'surveys', 'view', NOW(), NOW()),
  ('personero:direccion_general', 'voting-tables', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:direccion_general', 'election-events', 'view', NOW(), NOW()),
  ('personero:direccion_general', 'voting-locations', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:direccion_general', 'personeros', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:direccion_general', 'candidate-management', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:personero_legal', 'vote-counting', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:personero_legal', 'surveys', 'view', NOW(), NOW()),
  ('personero:personero_legal', 'voting-tables', 'view', NOW(), NOW()),
  ('personero:personero_legal', 'election-events', 'view', NOW(), NOW()),
  ('personero:personero_tecnico', 'vote-counting', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:personero_tecnico', 'surveys', 'view', NOW(), NOW()),
  ('personero:personero_tecnico', 'voting-tables', 'view', NOW(), NOW()),
  ('personero:personero_tecnico', 'election-events', 'view', NOW(), NOW()),
  ('personero:personero_nacional', 'vote-counting', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:personero_nacional', 'surveys', 'view', NOW(), NOW()),
  ('personero:personero_nacional', 'voting-tables', 'view', NOW(), NOW()),
  ('personero:personero_nacional', 'election-events', 'view', NOW(), NOW()),
  ('personero:coord_politico', 'vote-counting', 'view,create,update,delete,import,export', NOW(), NOW()),
  ('personero:coord_politico', 'surveys', 'view', NOW(), NOW()),
  ('personero:coord_politico', 'voting-tables', 'view', NOW(), NOW()),
  ('personero:coord_politico', 'election-events', 'view', NOW(), NOW());

-- Tras ejecutar y verificar, opcionalmente registrar la migracion como aplicada:
-- INSERT INTO `migrations` (`migration`, `batch`)
-- SELECT '2026_09_09_000002_create_role_permissions_table', COALESCE(MAX(`batch`), 0) + 1
-- FROM `migrations`
-- HAVING NOT EXISTS (
--   SELECT 1 FROM `migrations`
--   WHERE `migration` = '2026_09_09_000002_create_role_permissions_table'
-- );
-- El ambito geografico y la jerarquia de personeros NO cambian: el permiso dice
-- que se puede hacer en un modulo; el scope, sobre que datos.


-- ---------------------------------------------------------------------------
-- 2026-09-09: estado del candidato + candidatos automaticos por partido
-- ---------------------------------------------------------------------------
-- Al dar de alta un partido, la aplicacion crea un candidato por cada cargo
-- regional y municipal del pais con el nombre "-", para que solo haya que
-- escribir los nombres.
--
-- Estados: 'pending' (sin nombre, su partido compite ahi: sale su logo) ·
-- 'active' (con nombre) · 'inactive' (Inactivo: dado de baja, reversible) ·
-- 'disabled' (No participa: su partido no compite en ese cargo y lugar; ver el
-- bloque 7). Solo 'pending' y 'active' aparecen en encuestas y conteo. El borrado
-- logico se reserva para "esto fue un error".
--
-- En que lugares y a que cargos compite cada partido se decide con este mismo
-- estado, desde la pantalla "Participacion de partidos": no necesita tabla propia.
--
-- La columna nace con DEFAULT 'active' a proposito: asi los candidatos que ya
-- existen quedan activos sin necesidad de un UPDATE masivo.
-- Bloque idempotente: puede ejecutarse mas de una vez sin efecto.
SET @add_candidate_status_sql = (
  SELECT IF(
    COUNT(*) = 0,
    'ALTER TABLE `candidates` ADD COLUMN `status` varchar(12) NOT NULL DEFAULT ''active'' AFTER `photo`',
    'SELECT ''omitido: candidates.status ya existe'' AS info'
  )
  FROM INFORMATION_SCHEMA.COLUMNS
  WHERE TABLE_SCHEMA = DATABASE()
    AND TABLE_NAME = 'candidates'
    AND COLUMN_NAME = 'status'
);
PREPARE stmt FROM @add_candidate_status_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

SET @add_candidate_status_index_sql = (
  SELECT IF(
    COUNT(*) = 0,
    'ALTER TABLE `candidates` ADD INDEX `candidates_type_status_index` (`type`, `status`)',
    'SELECT ''omitido: candidates_type_status_index ya existe'' AS info'
  )
  FROM INFORMATION_SCHEMA.STATISTICS
  WHERE TABLE_SCHEMA = DATABASE()
    AND TABLE_NAME = 'candidates'
    AND INDEX_NAME = 'candidates_type_status_index'
);
PREPARE stmt FROM @add_candidate_status_index_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

-- Despues de este bloque, el bloque 6 crea los candidatos de los partidos que ya
-- existian (equivale a `php artisan candidates:generate-placeholders`).
--
-- Opcionalmente registrar la migracion como aplicada:
-- INSERT INTO `migrations` (`migration`, `batch`)
-- SELECT '2026_09_09_000003_add_status_to_candidates_table', COALESCE(MAX(`batch`), 0) + 1
-- FROM `migrations`
-- HAVING NOT EXISTS (
--   SELECT 1 FROM `migrations`
--   WHERE `migration` = '2026_09_09_000003_add_status_to_candidates_table'
-- );

-- ---------------------------------------------------------------------------
-- 2026-09-10: candidatos automaticos para los partidos que YA existen
-- ---------------------------------------------------------------------------
-- Al crear un partido, la aplicacion genera un candidato por cada cargo regional
-- y municipal del pais, con el nombre "-" y estado 'disabled' ("No participa").
-- Este bloque hace lo mismo con los partidos que ya estaban creados. Equivale
-- exactamente a `php artisan candidates:generate-placeholders`: se comprobo fila
-- por fila sobre una copia de la base (4 458 filas, 0 diferencias).
-- Hasta el 2026-09-11 los creaba en 'pending' (compitiendo en todo el pais); si
-- ya lo ejecutaste asi, el bloque 7 los pasa a 'disabled'.
--
-- Por cada partido vivo (no borrado logicamente) crea:
--   - 1 gobernador regional por departamento   (25; el exterior no elige)
--   - 1 consejero regional   por provincia     (196)
--   - 1 alcalde provincial   por provincia     (196)
--   - 1 alcalde distrital    por distrito      (1 892)
--
-- REQUIERE el bloque 5: sin la columna `candidates.status` falla con
-- "Unknown column 'status'".
--
-- Idempotente: solo crea lo que falta. Un puesto ya ocupado por cualquier
-- candidato de ese partido y cargo -tambien uno borrado logicamente o de baja- no
-- se repite, igual que en la aplicacion. Se puede volver a ejecutar tras anadir
-- geografia nueva. Las fechas van en UTC, como las guarda Laravel.
--
-- DESPUES: los partidos no compiten en ningun sitio hasta que el cliente lo marque
-- en la pantalla "Participacion de partidos" (casilla por casilla, o con los
-- botones "Todo el pais", "Solo aqui" y "Ninguno"). Volver a ejecutar este bloque
-- no deshace ese ajuste: los puestos ya existen y no se tocan; solo crea los de
-- geografia nueva, en "No participa".
--
-- Antes de ejecutarlo, cuantos crearia (vista previa, no escribe nada):
--
--   SELECT p.name AS partido, x.cargo, COUNT(*) AS se_crearian
--   FROM political_parties p
--   JOIN (
--     SELECT 'regional_governor' AS cargo, d.id AS geo, 'department' AS nivel
--       FROM departments d WHERE d.exterior = 0
--     UNION ALL SELECT 'regional_councilor', pr.id, 'province'
--       FROM provinces pr JOIN departments d ON d.id = pr.department_id AND d.exterior = 0
--     UNION ALL SELECT 'provincial_mayor', pr.id, 'province'
--       FROM provinces pr JOIN departments d ON d.id = pr.department_id AND d.exterior = 0
--     UNION ALL SELECT 'district_mayor', di.id, 'district'
--       FROM districts di JOIN provinces pr ON pr.id = di.province_id
--       JOIN departments d ON d.id = pr.department_id AND d.exterior = 0
--   ) x
--   WHERE p.deleted_at IS NULL
--     AND NOT EXISTS (
--       SELECT 1 FROM candidates c
--       WHERE c.political_party_id = p.id AND c.type = x.cargo
--         AND ((x.nivel = 'department' AND c.department_id = x.geo)
--           OR (x.nivel = 'province'   AND c.province_id   = x.geo)
--           OR (x.nivel = 'district'   AND c.district_id   = x.geo))
--     )
--   GROUP BY p.name, x.cargo ORDER BY p.name, x.cargo;
START TRANSACTION;

-- 1) Gobernador regional: uno por departamento (sin el exterior).
INSERT INTO `candidates`
  (`political_party_id`, `type`, `department_id`, `province_id`, `district_id`,
   `first_name`, `last_name`, `status`, `created_at`, `updated_at`)
SELECT p.`id`, 'regional_governor', d.`id`, NULL, NULL,
       '-', '-', 'disabled', UTC_TIMESTAMP(), UTC_TIMESTAMP()
FROM `political_parties` p
JOIN `departments` d ON d.`exterior` = 0
WHERE p.`deleted_at` IS NULL
  AND NOT EXISTS (
    SELECT 1 FROM `candidates` c
    WHERE c.`political_party_id` = p.`id`
      AND c.`type` = 'regional_governor'
      AND c.`department_id` = d.`id`
  );

-- 2) Consejero regional: uno por provincia.
INSERT INTO `candidates`
  (`political_party_id`, `type`, `department_id`, `province_id`, `district_id`,
   `first_name`, `last_name`, `status`, `created_at`, `updated_at`)
SELECT p.`id`, 'regional_councilor', pr.`department_id`, pr.`id`, NULL,
       '-', '-', 'disabled', UTC_TIMESTAMP(), UTC_TIMESTAMP()
FROM `political_parties` p
JOIN `provinces` pr
JOIN `departments` d ON d.`id` = pr.`department_id` AND d.`exterior` = 0
WHERE p.`deleted_at` IS NULL
  AND NOT EXISTS (
    SELECT 1 FROM `candidates` c
    WHERE c.`political_party_id` = p.`id`
      AND c.`type` = 'regional_councilor'
      AND c.`province_id` = pr.`id`
  );

-- 3) Alcalde provincial: uno por provincia.
INSERT INTO `candidates`
  (`political_party_id`, `type`, `department_id`, `province_id`, `district_id`,
   `first_name`, `last_name`, `status`, `created_at`, `updated_at`)
SELECT p.`id`, 'provincial_mayor', pr.`department_id`, pr.`id`, NULL,
       '-', '-', 'disabled', UTC_TIMESTAMP(), UTC_TIMESTAMP()
FROM `political_parties` p
JOIN `provinces` pr
JOIN `departments` d ON d.`id` = pr.`department_id` AND d.`exterior` = 0
WHERE p.`deleted_at` IS NULL
  AND NOT EXISTS (
    SELECT 1 FROM `candidates` c
    WHERE c.`political_party_id` = p.`id`
      AND c.`type` = 'provincial_mayor'
      AND c.`province_id` = pr.`id`
  );

-- 4) Alcalde distrital: uno por distrito.
INSERT INTO `candidates`
  (`political_party_id`, `type`, `department_id`, `province_id`, `district_id`,
   `first_name`, `last_name`, `status`, `created_at`, `updated_at`)
SELECT p.`id`, 'district_mayor', pr.`department_id`, di.`province_id`, di.`id`,
       '-', '-', 'disabled', UTC_TIMESTAMP(), UTC_TIMESTAMP()
FROM `political_parties` p
JOIN `districts` di
JOIN `provinces` pr ON pr.`id` = di.`province_id`
JOIN `departments` d ON d.`id` = pr.`department_id` AND d.`exterior` = 0
WHERE p.`deleted_at` IS NULL
  AND NOT EXISTS (
    SELECT 1 FROM `candidates` c
    WHERE c.`political_party_id` = p.`id`
      AND c.`type` = 'district_mayor'
      AND c.`district_id` = di.`id`
  );

COMMIT;

-- Comprobacion: cuantos marcadores tiene cada partido vivo (2 309 por partido,
-- menos los puestos que ya tenia ocupados).
-- SELECT p.name, c.type, COUNT(*) FROM candidates c
-- JOIN political_parties p ON p.id = c.political_party_id AND p.deleted_at IS NULL
-- WHERE c.first_name = '-' AND c.last_name = '-' AND c.deleted_at IS NULL
-- GROUP BY p.name, c.type ORDER BY p.name, c.type;


-- ---------------------------------------------------------------------------
-- 2026-09-11: la participacion de los partidos nace DESMARCADA  (bloque 7)
-- ---------------------------------------------------------------------------
-- El cliente pidio que en "Participacion de partidos" las casillas salgan
-- desmarcadas y las vaya habilitando el. Para eso el candidato tiene un cuarto
-- estado, 'disabled' ("No participa"): su partido no compite en ese cargo y
-- lugar. Solo lo pone y lo quita esa pantalla. Es distinto de 'inactive'
-- ("Inactivo"), que es dar de baja a una persona desde Candidatos.
--
-- Este bloque pasa a 'disabled' los candidatos SIN NOMBRE ("-") de los cuatro
-- cargos regionales y municipales: los que genero el bloque 6 (o la aplicacion
-- al crear un partido), y tambien los que la version anterior dejaba "de baja"
-- ('inactive') fuera de la region de un movimiento.
--
-- NO toca a ningun candidato con nombre: siguen Activos o Inactivos como estan,
-- y donde un partido ya tiene un candidato con nombre compitiendo, su casilla
-- sigue marcada. Tampoco toca los cargos de Elecciones Generales ni borra nada.
--
-- DESPUES, en la encuesta y en el conteo cada partido sale solo donde tenga un
-- candidato con nombre activo, hasta que el cliente marque donde compite en
-- "Participacion de partidos". Las respuestas y votos ya registrados no cambian.
-- Ejecutarlo junto con el despliegue de esta version del codigo.
--
-- Se aplica UNA sola vez: deja su marca en la tabla `migrations` y, si la marca
-- ya existe, no cambia nada. Asi volver a ejecutar el archivo NO desmarca lo que
-- el cliente haya marcado despues. Sin cambios de esquema (`status` es
-- varchar(12)). REQUIERE el bloque 5.
--
-- Vista previa (no escribe nada): cuantos pasarian a "No participa".
--   SELECT type, status, COUNT(*) AS n FROM candidates
--   WHERE type IN ('regional_governor','regional_councilor','provincial_mayor','district_mayor')
--     AND status IN ('pending','inactive')
--     AND TRIM(COALESCE(first_name,'')) IN ('','-')
--     AND TRIM(COALESCE(last_name,'')) IN ('','-')
--   GROUP BY type, status;
SET @participacion_desmarcada_aplicada = (
  SELECT COUNT(*) FROM `migrations`
  WHERE `migration` = '2026_09_11_000001_participation_off_by_default'
);

START TRANSACTION;

UPDATE `candidates`
SET `status` = 'disabled', `updated_at` = UTC_TIMESTAMP()
WHERE @participacion_desmarcada_aplicada = 0
  AND `type` IN ('regional_governor', 'regional_councilor', 'provincial_mayor', 'district_mayor')
  AND `status` IN ('pending', 'inactive')
  AND TRIM(COALESCE(`first_name`, '')) IN ('', '-')
  AND TRIM(COALESCE(`last_name`, '')) IN ('', '-');

-- La marca: tambien le dice a Artisan que la migracion equivalente ya se aplico.
INSERT INTO `migrations` (`migration`, `batch`)
SELECT '2026_09_11_000001_participation_off_by_default', COALESCE(MAX(`batch`), 0) + 1
FROM `migrations`
HAVING @participacion_desmarcada_aplicada = 0;

COMMIT;

-- Comprobacion: debe devolver 1 fila (la marca) y 0 candidatos sin nombre compitiendo
-- de los cuatro cargos, salvo los que el cliente ya haya habilitado despues.
--   SELECT migration, batch FROM migrations
--   WHERE migration = '2026_09_11_000001_participation_off_by_default';
--   SELECT status, COUNT(*) FROM candidates
--   WHERE type IN ('regional_governor','regional_councilor','provincial_mayor','district_mayor')
--     AND first_name = '-' AND last_name = '-' GROUP BY status;

-- Nota: el bloque 7 tambien alcanza a los marcadores borrados logicamente (quedan en
-- "No participa"), y no distingue un nombre que despues de TRIM se queda en "-": para
-- el sistema eso ES un candidato sin nombre en todas las pantallas. Ejecutalo una sola
-- vez a la vez (una consola), no dos en paralelo.


-- ---------------------------------------------------------------------------
-- 2026-09-11: indices por sitio del candidato  (bloque 8)
-- ---------------------------------------------------------------------------
-- Equivale a: 2026_09_11_000002_add_slot_indexes_to_candidates.php
--
-- El "sitio" de un candidato es su cargo + su partido + su lugar. Es justo lo que
-- consulta la aplicacion para saber si un partido ya tiene ahi un candidato con
-- nombre (y no repetirlo con su logo). Sin estos indices, MySQL recorre el indice de
-- `type` entero en cada comprobacion.
--
-- Medido sobre una copia de produccion inflada a 33 partidos (~78 000 candidatos, con
-- 10 partidos compitiendo en todo el pais):
--   - primera pagina de Alcaldes Distritales: 833 ms -> 180 ms
--   - conteo de la pestana Activos (nacional): 458 ms -> 82 ms
--   - lo mismo para consejeros regionales:     359 ms ->  7 ms
-- Con los ~11 400 candidatos de hoy la pantalla ya va rapida; esto es para cuando el
-- cliente registre el resto de partidos.
--
-- Bloque idempotente: cada indice se crea solo si no existe. Tarda unos segundos y no
-- bloquea la tabla (ALGORITHM=INPLACE por defecto en InnoDB).
SET @idx_sitio_departamento = (
  SELECT IF(COUNT(*) = 0,
    'ALTER TABLE `candidates` ADD INDEX `candidates_slot_department_index` (`type`, `political_party_id`, `department_id`)',
    'SELECT ''omitido: candidates_slot_department_index ya existe'' AS info')
  FROM INFORMATION_SCHEMA.STATISTICS
  WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'candidates' AND INDEX_NAME = 'candidates_slot_department_index'
);
PREPARE stmt FROM @idx_sitio_departamento; EXECUTE stmt; DEALLOCATE PREPARE stmt;

SET @idx_sitio_provincia = (
  SELECT IF(COUNT(*) = 0,
    'ALTER TABLE `candidates` ADD INDEX `candidates_slot_province_index` (`type`, `political_party_id`, `province_id`)',
    'SELECT ''omitido: candidates_slot_province_index ya existe'' AS info')
  FROM INFORMATION_SCHEMA.STATISTICS
  WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'candidates' AND INDEX_NAME = 'candidates_slot_province_index'
);
PREPARE stmt FROM @idx_sitio_provincia; EXECUTE stmt; DEALLOCATE PREPARE stmt;

SET @idx_sitio_distrito = (
  SELECT IF(COUNT(*) = 0,
    'ALTER TABLE `candidates` ADD INDEX `candidates_slot_district_index` (`type`, `political_party_id`, `district_id`)',
    'SELECT ''omitido: candidates_slot_district_index ya existe'' AS info')
  FROM INFORMATION_SCHEMA.STATISTICS
  WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'candidates' AND INDEX_NAME = 'candidates_slot_district_index'
);
PREPARE stmt FROM @idx_sitio_distrito; EXECUTE stmt; DEALLOCATE PREPARE stmt;

-- Tras crear o rellenar muchos candidatos conviene refrescar las estadisticas para que
-- el optimizador elija bien desde el primer momento (no cambia datos).
ANALYZE TABLE `candidates`;

-- Opcionalmente registrar la migracion como aplicada:
-- INSERT INTO `migrations` (`migration`, `batch`)
-- SELECT '2026_09_11_000002_add_slot_indexes_to_candidates', COALESCE(MAX(`batch`), 0) + 1
-- FROM `migrations`
-- HAVING NOT EXISTS (
--   SELECT 1 FROM `migrations`
--   WHERE `migration` = '2026_09_11_000002_add_slot_indexes_to_candidates'
-- );
