<?php

namespace Config;

// Create a new instance of our RouteCollection class.
$routes = Services::routes();

// Load the system's routing file first, so that the app and ENVIRONMENT
// can override as needed.
if (file_exists(SYSTEMPATH . 'Config/Routes.php')) {
    require SYSTEMPATH . 'Config/Routes.php';
}

/*
 * --------------------------------------------------------------------
 * Router Setup
 * --------------------------------------------------------------------
 */
$routes->setDefaultNamespace('App\Controllers');
$routes->setDefaultController('Dashboard');
$routes->setDefaultMethod('index');
$routes->setTranslateURIDashes(false);
$routes->set404Override(function() {
    $request = \Config\Services::request();
    log_message('error', '404 - URI: ' . $request->getUri()->getPath());
    log_message('error', '404 - Method: ' . $request->getMethod());

    // If it's an API request, send JSON and exit (do not return Response - framework echoes Closure return value)
    if (strpos($request->getUri()->getPath(), 'api/') !== false) {
        $response = \Config\Services::response();
        $response->setStatusCode(404)->setJSON([
            'success' => false,
            'message' => 'API endpoint not found: ' . $request->getUri()->getPath()
        ]);
        $response->send();
        exit;
    }

    throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
});
// Auto-routing is disabled: filters (including adops) only apply to explicitly
// registered routes. Auto-routing would allow URL patterns like
// /ImportExport/uploadCampaigns to bypass the adops filter and expose
// ad-ops mutations to regular advertisers behind only the auth filter.
$routes->setAutoRoute(false);

/*
 * --------------------------------------------------------------------
 * Route Definitions
 * --------------------------------------------------------------------
 */

// We get a performance increase by specifying the default
// route since we don't have to scan directories.

// Auth routes
$routes->get('login', 'Users::login', ['filter' => 'noauth']);
$routes->post('login', 'Users::auth', ['filter' => 'noauth']);
$routes->add('logout', 'Users::logout');
$routes->post('auth', 'Users::auth', ['filter' => 'noauth']);
$routes->post('forgot-password', 'Users::forgotPassword', ['filter' => 'noauth']);
$routes->get('reset-password', 'Users::resetPasswordForm', ['filter' => 'noauth']);
$routes->post('reset-password', 'Users::resetPassword', ['filter' => 'noauth']);
$routes->get('verify-2fa', 'Users::verify2faForm', ['filter' => 'noauth']);
$routes->post('verify-2fa', 'Users::verify2fa', ['filter' => 'noauth']);
$routes->post('resend-2fa-key', 'Users::resend2faKey', ['filter' => 'noauth']);

// Dashboard (GET + POST for filters / refresh)
$routes->get('/', 'Dashboard::index', ['filter' => 'auth']);
$routes->match(['GET', 'POST'], 'dashboard', 'Dashboard::index', ['filter' => 'auth']);
$routes->match(['GET', 'POST'], 'campaign-performance', 'CampaignPerformance::index', ['filter' => 'auth']);
$routes->get('campaign-performance/states', 'CampaignPerformance::stateOptions', ['filter' => 'auth']);
$routes->get('campaign-performance/csv', 'CampaignPerformance::csvExport', ['filter' => 'auth']);

// AP-103 — Hearst Pacing Report (Hearst-family advertisers only)
$routes->get('pacing-report', 'PacingReport::index', ['filter' => ['auth', 'hearst']]);
$routes->match(['GET', 'POST'], 'pacing-report/json', 'PacingReport::getPaceJSON', ['filter' => ['auth', 'hearst']]);
$routes->get('pacing-report/csv', 'PacingReport::csvExport', ['filter' => ['auth', 'hearst']]);

// AP-103 — Categorized Inventory (Hearst-family + sibling-program advertisers)
$routes->get('categorized-inventory', 'CategorizedInventory::index', ['filter' => ['auth', 'hearst:inventory']]);
$routes->post('api/categorized-inventory/domains', 'CategorizedInventory::getDomains', ['filter' => ['auth', 'hearst:inventory']]);
$routes->post('api/categorized-inventory/domain-slugs', 'CategorizedInventory::getDomainSlugs', ['filter' => ['auth', 'hearst:inventory']]);

// AP-103 — Behavior Report (Hearst-family only)
$routes->match(['GET', 'POST'], 'behavior-report', 'BehaviorReport::index', ['filter' => ['auth', 'hearst']]);
$routes->match(['GET', 'POST'], 'behavior-report/insights', 'BehaviorReport::insights', ['filter' => ['auth', 'hearst']]);

// AP-103 — Podcast Report (Hearst-family only)
$routes->match(['GET', 'POST'], 'podcast-report', 'PodcastReport::index', ['filter' => ['auth', 'hearst']]);
$routes->get('podcast-report/csv', 'PodcastReport::csvExport', ['filter' => ['auth', 'hearst']]);

// UI Kit demo page (design components)
$routes->get('ui-kit', 'UiKit::index', ['filter' => 'auth']);
$routes->get('ui-kit/docs', 'UiKit::docs', ['filter' => 'auth']);
// Campaign Management route
$routes->get('campaigns', 'Campaign::management', ['filter' => 'auth']);
$routes->post('api/campaign-search', 'Campaign::search', ['filter' => 'auth']);
$routes->post('api/campaign/history/(:num)', 'Campaign::getHistory/$1', ['filter' => 'auth']);
$routes->get('api/campaign/(:num)/linked-creatives', 'Campaign::getLinkedCreatives/$1', ['filter' => 'auth']);
$routes->get('api/campaign/(:num)/linked-lists/(:alpha)', 'Campaign::getLinkedLists/$1/$2', ['filter' => 'auth']);

// Geo targeting AJAX endpoints
$routes->get('api/geo/dma',    'Campaign::geoDma',    ['filter' => 'auth']);
$routes->get('api/geo/cities', 'Campaign::geoCities', ['filter' => 'auth']);
$routes->get('api/geo/zips',   'Campaign::geoZips',   ['filter' => 'auth']);

// Creative Management routes
$routes->get('creatives', 'Creative::management', ['filter' => 'auth']);
$routes->get('creative/create', 'Creative::create', ['filter' => 'auth']);
$routes->post('creative/store', 'Creative::store', ['filter' => 'editor']);
// AP-117: only the edit FORM (GET) stays on `auth` so the controller can run
// the adv-19880 viewer carve-out (legacy Creatives.php:191 — viewers of 19880 may
// open the edit form). Saving (`update`) and `store` block ALL viewers via the
// `editor` filter, matching legacy store()/update() which have no 19880 exception.
$routes->get('creative/edit/(:num)', 'Creative::edit/$1', ['filter' => 'auth']);
$routes->post('creative/update/(:num)', 'Creative::update/$1', ['filter' => 'editor']);
// Creative routes - old-style aliases
$routes->get('creatives/create', 'Creative::create', ['filter' => 'auth']);
$routes->post('creatives/create', 'Creative::store', ['filter' => 'editor']);
$routes->get('creatives/(:num)/edit', 'Creative::edit/$1', ['filter' => 'auth']);
$routes->post('creatives/(:num)/edit', 'Creative::update/$1', ['filter' => 'editor']);
$routes->match(['GET', 'POST'], 'api/creative/list', 'Creative::getCreativesList', ['filter' => 'auth']);
$routes->match(['GET', 'POST'], 'api/creative/filter', 'Creative::getCreativesFilter', ['filter' => 'auth']);
$routes->post('api/creative/bulk-delete', 'Creative::bulkDelete', ['filter' => 'editor']);
$routes->post('api/creative/bulk-denied', 'Creative::bulkDenied', ['filter' => 'editor']);
$routes->post('api/creative/bulk-pause', 'Creative::bulkPause', ['filter' => 'editor']);
$routes->post('api/creative/bulk-resume', 'Creative::bulkResume', ['filter' => 'editor']);
$routes->post('api/creative/bulk-clone', 'Creative::bulkClone', ['filter' => 'editor']);
// Unified bulk text update (Destination/Headline/Description/Display URL × complete/partial/append/remove) — prod parity.
$routes->post('api/creative/bulk-field-update', 'Creative::bulkCreativeTextChange', ['filter' => 'editor']);
$routes->get('api/creative/(:num)/linked-campaigns', 'Creative::getLinkedCampaigns/$1', ['filter' => 'auth']);
$routes->get('api/creative/(:num)', 'Creative::getCreative/$1', ['filter' => 'auth']);
$routes->post('api/creative/history/(:num)', 'Creative::getHistory/$1', ['filter' => 'auth']);

// Campaign Creation routes — campaigns/create/[type] (6 types, GET shows form, POST saves)
// GET + POST both editor-gated so View users cannot open create forms then fail on submit.
$routes->get( 'campaigns/create/display',     'Campaign::createDisplay',    ['filter' => 'editor']);
$routes->post('campaigns/create/display',     'Campaign::save',             ['filter' => 'editor']);
$routes->get( 'campaigns/create/text-search', 'Campaign::createTextSearch', ['filter' => 'editor']);
$routes->post('campaigns/create/text-search', 'Campaign::save',             ['filter' => 'editor']);
$routes->get( 'campaigns/create/text-pop',    'Campaign::createTextPop',    ['filter' => 'editor']);
$routes->post('campaigns/create/text-pop',    'Campaign::save',             ['filter' => 'editor']);
$routes->get( 'campaigns/create/native',      'Campaign::createNative',     ['filter' => 'editor']);
$routes->post('campaigns/create/native',      'Campaign::save',             ['filter' => 'editor']);
$routes->get( 'campaigns/create/shopping',    'Campaign::createShopping',   ['filter' => 'editor']);
$routes->post('campaigns/create/shopping',    'Campaign::save',             ['filter' => 'editor']);
$routes->get( 'campaigns/create/swicki',      'Campaign::createSwicki',     ['filter' => 'editor']);
$routes->post('campaigns/create/swicki',      'Campaign::save',             ['filter' => 'editor']);
// Social Media (Facebook) campaign wizard — native CI4 form proxying to v1/sm.
$routes->get( 'campaigns/create/social',  'SocialCampaign::create',       ['filter' => 'editor']);
$routes->get( 'campaigns/fb-(:num)/edit', 'SocialCampaign::edit/$1',      ['filter' => 'editor']);
$routes->get( 'creatives/create/social',  'SocialCampaign::creativeForm', ['filter' => 'editor']);
// Wizard reference-data proxies (GET)
$routes->get( 'social-campaign/pages',                'SocialCampaign::pages',               ['filter' => 'auth']);
$routes->get( 'social-campaign/adsets',               'SocialCampaign::adsets',              ['filter' => 'auth']);
$routes->get( 'social-campaign/targeting-search',     'SocialCampaign::targetingSearch',     ['filter' => 'auth']);
$routes->get( 'social-campaign/targeting-suggestions','SocialCampaign::targetingSuggestions',['filter' => 'auth']);
$routes->get( 'social-campaign/targeting-browse',     'SocialCampaign::targetingBrowse',     ['filter' => 'auth']);
$routes->get( 'social-campaign/delivery-estimate',    'SocialCampaign::deliveryEstimate',    ['filter' => 'auth']);
$routes->post('social-campaign/location-search',      'SocialCampaign::locationSearch',      ['filter' => 'auth']);
// Writes (POST)
$routes->post('social-campaign/store',                'SocialCampaign::store',               ['filter' => 'editor']);
$routes->post('social-campaign/status/(:num)',        'SocialCampaign::status/$1',           ['filter' => 'editor']);
$routes->post('social-campaign/upload-image',         'SocialCampaign::uploadImage',         ['filter' => 'editor']);
$routes->post('social-campaign/creative-store',       'SocialCampaign::creativeStore',       ['filter' => 'editor']);
$routes->post('social-campaign/link-page',            'SocialCampaign::linkPage',            ['filter' => 'editor']);
// Campaign Edit route
$routes->get( 'campaign/edit/(:num)',    'Campaign::edit/$1',   ['filter' => 'editor']);
$routes->post('campaign/update/(:num)',  'Campaign::update/$1', ['filter' => 'editor']);

// Campaign API endpoints
$routes->match(['GET', 'POST'], 'api/campaign/list', 'Campaign::getCampaignsList', ['filter' => 'auth']);
$routes->match(['GET', 'POST'], 'api/campaign/filter', 'Campaign::getCampaignsFilter', ['filter' => 'auth']);
$routes->post('campaign/save', 'Campaign::save', ['filter' => 'editor']);
$routes->get('api/campaign/categories', 'Campaign::getCategories', ['filter' => 'auth']);
$routes->get('api/campaign/creatives', 'Campaign::getCreatives', ['filter' => 'auth']);
$routes->get('api/campaign/countries', 'Campaign::getCountries', ['filter' => 'auth']);
$routes->get('api/campaign/keyword-sources', 'Campaign::getKeywordSources', ['filter' => 'auth']);
$routes->post('api/campaign/bulk-pause', 'Campaign::bulkPause', ['filter' => 'editor']);
$routes->post('api/campaign/bulk-resume', 'Campaign::bulkResume', ['filter' => 'editor']);
$routes->post('api/campaign/bulk-delete', 'Campaign::bulkDelete', ['filter' => 'editor']);
$routes->post('api/campaign/bulk-denied', 'Campaign::bulkDenied', ['filter' => 'editor']);
$routes->post('api/campaign/bulk-clone', 'Campaign::bulkClone', ['filter' => 'editor']);
$routes->post('api/campaign/bulk-budget', 'Campaign::bulkUpdateBudget', ['filter' => 'editor']);
$routes->post('api/campaign/bulk-bid', 'Campaign::bulkUpdateBid', ['filter' => 'editor']);

// Account Users (AP-41) — AP-117: viewer-blocked whole page + writes
$routes->get('users', 'AccountUsers::index', ['filter' => 'editor']);
$routes->post('users/create', 'AccountUsers::create', ['filter' => 'editor']);
$routes->post('users/(:num)/edit', 'AccountUsers::edit/$1', ['filter' => 'editor']);

// Billing (AP-39) — AP-117: viewer-blocked whole page + deposit
$routes->get('billing', 'Billing::index', ['filter' => 'editor']);
$routes->post('billing/deposit-funds', 'Billing::depositFunds', ['filter' => 'editor']);

// Conversion Tracking / Pixels (AP-CT) — AP-117: viewer-blocked page + create
$routes->get('conversion-tracking', 'ConversionTracking::index', ['filter' => 'editor']);
$routes->post('conversion-tracking/create-pixel', 'ConversionTracking::createRetargetingPixel', ['filter' => 'editor']);

// API Access (AP-CT)
$routes->get('api-access', 'ApiAccess::index', ['filter' => 'auth']);

// Potential Reach Tool
$routes->get('potential-reach', 'PotentialReach::index', ['filter' => 'auth']);
$routes->get('potential-reach/delivery-estimate', 'PotentialReach::deliveryEstimate', ['filter' => 'auth']);
$routes->get('potential-reach/targeting-search', 'PotentialReach::targetingSearch', ['filter' => 'auth']);
$routes->post('potential-reach/location-search', 'PotentialReach::locationSearch', ['filter' => 'auth']);
$routes->post('potential-reach/geo-meta', 'PotentialReach::geoMeta', ['filter' => 'auth']);
$routes->get('potential-reach/targeting-suggestions', 'PotentialReach::targetingSuggestions', ['filter' => 'auth']);
$routes->get('potential-reach/targeting-browse', 'PotentialReach::targetingBrowse', ['filter' => 'auth']);

// Account Settings
$routes->get('account-settings', 'AccountSettings::index', ['filter' => 'auth']);
$routes->post('account-settings/update', 'AccountSettings::update', ['filter' => 'editor']);
// AP-117: password change stays self-service (viewers may change their own password).
$routes->post('account-settings/update-password', 'AccountSettings::updatePassword', ['filter' => 'auth']);

/*
 * Breakdown view routes
 */
$routes->get('breakdown-view', 'BreakdownView::index', ['filter' => 'auth']);
$routes->post('breakdown-view', 'BreakdownView::index', ['filter' => 'auth']);
$routes->get('breakdown-view/(:segment)', 'BreakdownView::index/$1', ['filter' => 'auth']);
$routes->post('breakdown-view/(:segment)', 'BreakdownView::index/$1', ['filter' => 'auth']);

// Breakdown view API endpoints (single endpoint; type comes in as breakdown_type)
$routes->match(['GET', 'POST'], 'api/breakdown/getBreakdownData', 'BreakdownView::getBreakdownData', ['filter' => 'auth']);
$routes->get('api/breakdown/getBreakdownTableData', 'BreakdownView::getBreakdownTableData', ['filter' => 'auth']);
$routes->get('breakdown-view/benchmark-heatwave', 'BreakdownView::benchmarkHeatwave', ['filter' => 'auth']);

// Alerts Manager (AP-31) — Ad Ops / mngt users only
$routes->get('alerts-manager', 'AlertsManager::index', ['filter' => 'adops']);
$routes->post('alerts-manager/create', 'AlertsManager::create', ['filter' => 'adops']);
$routes->post('alerts-manager/update', 'AlertsManager::update', ['filter' => 'adops']);
$routes->post('alerts-manager/toggle-active', 'AlertsManager::toggleActive', ['filter' => 'adops']);
$routes->post('alerts-manager/delete', 'AlertsManager::delete', ['filter' => 'adops']);
// Error log viewer — mngt-only, no navigation link exposed.
$routes->get('logs', 'Logs::index', ['filter' => 'adops']);
$routes->post('logs/clear', 'Logs::clear', ['filter' => 'adops']);
// Promo Calendar — AP-110: mngt-only (legacy default.php:293-299 nav gate).
// `adops` filter enforces it at the URL level too (legacy had no controller gate).
$routes->get('promo-calendar', 'PromoCalendar::index', ['filter' => 'adops']);
$routes->post('promo-calendar/json', 'PromoCalendar::getJson', ['filter' => 'adops']);
$routes->post('promo-calendar/upload', 'PromoCalendar::upload', ['filter' => 'adops']);
$routes->get('promo-calendar/download/(:num)', 'PromoCalendar::download/$1', ['filter' => 'adops']);
$routes->post('promo-calendar/delete', 'PromoCalendar::delete', ['filter' => 'adops']);
$routes->post('promo-calendar/restore', 'PromoCalendar::restore', ['filter' => 'adops']);

// Ad Schedule (AP-99)
$routes->get('ad-schedule', 'AdSchedule::index', ['filter' => 'auth']);

// Checklist — AP-110: mngt-only (legacy default.php:301-307 nav gate + viewer-exit
// on create/store/edit/update). `adops` filter enforces it at the URL level.
$routes->get('checklist', 'Checklist::index', ['filter' => 'adops']);
$routes->get('checklist/create', 'Checklist::create', ['filter' => 'adops']);
$routes->post('checklist/store', 'Checklist::store', ['filter' => 'adops']);
$routes->get('checklist/(:num)/edit', 'Checklist::edit/$1', ['filter' => 'adops']);
$routes->post('checklist/(:num)/update', 'Checklist::update/$1', ['filter' => 'adops']);
$routes->post('checklist/(:num)/delete', 'Checklist::delete/$1', ['filter' => 'adops']);
$routes->get('api/checklist/json', 'Checklist::json', ['filter' => 'adops']);

// Affiliate Feed Management (gated by FeedManagementFilter — allowlist + mngt users)
$routes->get( 'feed-management',                   'FeedManagement::index',         ['filter' => 'feedmanagement']);
$routes->get( 'feed-management/(:num)',            'FeedManagement::index/$1',      ['filter' => 'feedmanagement']);
$routes->post('feed-management/store',             'FeedManagement::store',         ['filter' => 'feedmanagement']);
$routes->post('feed-management/(:num)/update',     'FeedManagement::update/$1',     ['filter' => 'feedmanagement']);
$routes->post('feed-management/(:num)/delete',     'FeedManagement::delete/$1',     ['filter' => 'feedmanagement']);
$routes->get( 'api/feed-management/json',          'FeedManagement::json',          ['filter' => 'feedmanagement']);

// Custom Reports
$routes->get('reports', 'Reports::index', ['filter' => 'auth']);
$routes->get('reports/create', 'Reports::create', ['filter' => 'auth']);
$routes->post('reports/store', 'Reports::store', ['filter' => 'editor']);
$routes->get('reports/(:num)/edit', 'Reports::edit/$1', ['filter' => 'auth']);
$routes->post('reports/(:num)/update', 'Reports::update/$1', ['filter' => 'editor']);
$routes->post('reports/(:num)/save-as', 'Reports::saveAs/$1', ['filter' => 'editor']);
$routes->post('reports/(:num)/delete', 'Reports::delete/$1', ['filter' => 'editor']);
$routes->post('reports/(:num)/schedule', 'Reports::schedule/$1', ['filter' => 'editor']);
$routes->get('api/custom-report/lists', 'Reports::json');
// Campaigns filtered by campaign-type label (display, text-pop, text-search,
// native, shopping, adx) — used by the Reports create/edit form's Campaign
// Type ↔ Campaign multi-select. Mirrors legacy campaigns/getJSONList/{type}.
// (:segment) lets hyphens through; an empty trailing param returns all.
$routes->get('api/custom-report/campaigns/(:segment)', 'Reports::campaignsByType/$1', ['filter' => 'auth']);
$routes->get('api/custom-report/campaigns',           'Reports::campaignsByType',     ['filter' => 'auth']);
// Scheduled-email cron HTTP entry-point — public, secret-gated inside the action.
// MUST be declared before reports/(:num) below or the (:num) match would swallow
// any numeric path segment after /reports/.
$routes->get('reports/cron', 'Reports::cron');
// Legacy-shape alias for external cron wrappers that still speak the CI3
// URL contract: /generate-reports/email/<adv>/<freq>/<sent>?pass=<secret>.
// Delegates to the same ScheduledReportsMailer::emailRun() as reports/cron.
$routes->get('generate-reports/email/(:num)/(:segment)/(:segment)',
    'Reports::legacyEmailRun/$1/$2/$3');
// Generated file viewer / download (mirrors legacy /reports/:id and /:id/:fid)
$routes->get('reports/(:num)', 'Reports::show/$1', ['filter' => 'auth']);
$routes->get('reports/(:num)/files/(:num)', 'Reports::showFile/$1/$2', ['filter' => 'auth']);
$routes->get('reports/(:num)/files/(:num)/csv', 'Reports::downloadFile/$1/$2', ['filter' => 'auth']);

$routes->post('ad-schedule/get-schedules', 'AdSchedule::getSchedules', ['filter' => 'auth']);
$routes->post('ad-schedule/get-schedule-history', 'AdSchedule::getScheduleHistory', ['filter' => 'auth']);
$routes->post('ad-schedule/get-creative-schedule-history', 'AdSchedule::getCreativeScheduleHistory', ['filter' => 'auth']);
$routes->post('ad-schedule/search-creatives', 'AdSchedule::searchCreatives', ['filter' => 'auth']);
$routes->post('ad-schedule/add', 'AdSchedule::add', ['filter' => 'auth']);
$routes->post('ad-schedule/update/(:num)', 'AdSchedule::update/$1', ['filter' => 'auth']);
$routes->post('ad-schedule/delete/(:num)', 'AdSchedule::delete/$1', ['filter' => 'auth']);

// Import / Export (AP-29) — AP-110: legacy parity = visible to any non-viewer
// advertiser (legacy ImportExport __construct viewer-exit), so `editor`. The
// email-schedule sub-feature stays mngt-only (legacy showEmailSchedule gate).
$routes->get('import-export', 'ImportExport::index', ['filter' => 'editor']);
$routes->get('import-export/import', 'ImportExport::import', ['filter' => 'editor']);
$routes->get('import-export/export', 'ImportExport::export', ['filter' => 'editor']);
$routes->get('import-export/download/campaigns', 'ImportExport::downloadCampaigns', ['filter' => 'editor']);
$routes->get('import-export/download/creatives', 'ImportExport::downloadCreatives', ['filter' => 'editor']);
$routes->get('import-export/download/bak', 'ImportExport::downloadBak', ['filter' => 'editor']);
$routes->post('import-export/upload/campaigns', 'ImportExport::uploadCampaigns', ['filter' => 'editor']);
$routes->post('import-export/upload/creatives', 'ImportExport::uploadCreatives', ['filter' => 'editor']);
$routes->post('import-export/upload/zip', 'ImportExport::uploadCampaignZips', ['filter' => 'editor']);
$routes->post('import-export/upload/dma', 'ImportExport::uploadCampaignDMA', ['filter' => 'editor']);
$routes->post('import-export/schedule-emails', 'ImportExport::scheduleEmails', ['filter' => 'adops']);
$routes->post('import-export/change-status', 'ImportExport::changeStatus', ['filter' => 'editor']);

// Lists (AP-30) — AP-110: legacy parity = visible to any non-viewer advertiser
// (legacy lists/* controllers viewer-exit), so gated by `editor` not `adops`.
$routes->get('lists', 'Lists::index', ['filter' => 'editor']);
// Domain Lists
$routes->get('lists/domains', 'Lists\Domains::json', ['filter' => 'editor']);
$routes->get('lists/domains/json/(:num)', 'Lists\Domains::show/$1', ['filter' => 'editor']);
// Per-list CSV download (legacy parity: lists/domains/<id>/csv).
$routes->get('lists/domains/(:num)/csv', 'Lists\Domains::downloadCsv/$1', ['filter' => 'editor']);
// Prod-parity URL: clicking a domain-list id in Campaign Manager (or an old
// `/lists/domains/<id>` bookmark) lands on the Lists hub with the Edit modal
// for that list auto-opened, mirroring the CI3 detail view.
$routes->get('lists/domains/(:num)', 'Lists::openDomains/$1', ['filter' => 'editor']);
$routes->post('lists/domains/create', 'Lists\Domains::create', ['filter' => 'editor']);
$routes->post('lists/domains/update/(:num)', 'Lists\Domains::update/$1', ['filter' => 'editor']);
$routes->post('lists/domains/delete/(:num)', 'Lists\Domains::delete/$1', ['filter' => 'editor']);
$routes->post('lists/domains/delete-rows', 'Lists\Domains::deleteRows', ['filter' => 'editor']);
$routes->post('lists/domains/upload', 'Lists\Domains::upload', ['filter' => 'editor']);
// Keyword Lists
$routes->get('lists/keyword-lists', 'Lists\KeywordLists::json', ['filter' => 'editor']);
$routes->get('lists/keyword-lists/json/(:num)', 'Lists\KeywordLists::show/$1', ['filter' => 'editor']);
// Per-list CSV download (legacy parity: lists/keywords/<id>/csv → here under keyword-lists).
$routes->get('lists/keyword-lists/(:num)/csv', 'Lists\KeywordLists::downloadCsv/$1', ['filter' => 'editor']);
$routes->get('lists/keyword-lists/(:num)', 'Lists::openKeywords/$1', ['filter' => 'editor']);
$routes->post('lists/keyword-lists/create', 'Lists\KeywordLists::create', ['filter' => 'editor']);
$routes->post('lists/keyword-lists/update/(:num)', 'Lists\KeywordLists::update/$1', ['filter' => 'editor']);
$routes->post('lists/keyword-lists/delete/(:num)', 'Lists\KeywordLists::delete/$1', ['filter' => 'editor']);
$routes->post('lists/keyword-lists/upload-mapping', 'Lists\KeywordLists::uploadMapping', ['filter' => 'editor']);
$routes->get('lists/keyword-lists/download-all', 'Lists\KeywordLists::downloadAllAsZip', ['filter' => 'editor']);
$routes->get('lists/keyword-lists/template', 'Lists\KeywordLists::downloadTemplate', ['filter' => 'editor']);
// Flat Keywords
$routes->get('lists/keywords', 'Lists\Keywords::json', ['filter' => 'editor']);
// Prod-parity URL: CI3 used `/lists/keywords/<id>` for the per-keyword-list
// detail view. We don't have a separate detail page in CI4, so redirect to
// the keyword-lists tab with that list's Edit modal auto-opened — keeps old
// bookmarks and the Campaign Manager linked-lists modal working.
$routes->get('lists/keywords/(:num)', 'Lists::openKeywords/$1', ['filter' => 'editor']);
$routes->post('lists/keywords/delete', 'Lists\Keywords::delete', ['filter' => 'editor']);
$routes->post('lists/keywords/find-replace', 'Lists\Keywords::findAndReplace', ['filter' => 'editor']);
// Sources
$routes->get('lists/sources', 'Lists\Sources::json', ['filter' => 'editor']);
$routes->get('lists/sources/json/(:num)', 'Lists\Sources::show/$1', ['filter' => 'editor']);
// Per-list CSV download (legacy parity: lists/sources/<id>/csv).
$routes->get('lists/sources/(:num)/csv', 'Lists\Sources::downloadCsv/$1', ['filter' => 'editor']);
$routes->get('lists/sources/(:num)', 'Lists::openSources/$1', ['filter' => 'editor']);
$routes->post('lists/sources/create', 'Lists\Sources::create', ['filter' => 'editor']);
$routes->post('lists/sources/update/(:num)', 'Lists\Sources::update/$1', ['filter' => 'editor']);
$routes->post('lists/sources/delete/(:num)', 'Lists\Sources::delete/$1', ['filter' => 'editor']);
$routes->post('lists/sources/delete-rows', 'Lists\Sources::deleteRows', ['filter' => 'editor']);

// Edit-history modal (QA-L2) — one endpoint that maps list "kind"
// (`domains`/`keyword-lists`/`keywords`/`sources`) to the correct set of
// entity strings in `shorty.adcenter_log`.
$routes->post('lists/history/(:segment)/(:num)', 'Lists::getHistory/$1/$2', ['filter' => 'editor']);

// Advertiser Assets
$routes->get('advertiser-assets', 'AdvertiserAssets::index', ['filter' => 'adops']);
$routes->post('advertiser-assets/upload', 'AdvertiserAssets::upload', ['filter' => 'adops']);
$routes->post('advertiser-assets/delete/(:num)', 'AdvertiserAssets::delete/$1', ['filter' => 'adops']);
// AP-117: was unfiltered — any unauthenticated caller could fetch advertiser
// asset files by enumerating the segment. Require authentication (kept at `auth`,
// not `adops`, since these assets may be embedded in advertiser-facing views).
$routes->get('asset/advertiser/(:segment)', 'Asset::advertiser/$1', ['filter' => 'auth']);

// ───────────────── AP-117 page migrations + SSO ─────────────────

// Amazon SSO — additive to 2FA; callback path `mslogin/(:any)` mirrors
// the legacy path the external oauth.admedia.com service redirects back to.
$routes->get('sso/start', 'Sso::start', ['filter' => 'noauth']);
$routes->get('mslogin/(:any)', 'Sso::callback/$1', ['filter' => 'noauth']);

// Notes — mngt-only CRUD; campaign-scoped read available to any auth user.
$routes->get('notes', 'Notes::index', ['filter' => 'adops']);
$routes->post('notes/create', 'Notes::create', ['filter' => 'adops']);
$routes->post('notes/(:num)/delete', 'Notes::delete/$1', ['filter' => 'adops']);
$routes->get('api/notes/campaign/(:num)', 'Notes::campaignNotes/$1', ['filter' => 'auth']);

// Campaign Categories — mngt-only.
$routes->get('campaign-categories', 'CampaignCategories::index', ['filter' => 'adops']);
$routes->get('campaign-categories/json', 'CampaignCategories::json', ['filter' => 'adops']);
$routes->post('campaign-categories/store', 'CampaignCategories::store', ['filter' => 'adops']);
$routes->post('campaign-categories/(:num)/delete', 'CampaignCategories::delete/$1', ['filter' => 'adops']);
$routes->get('campaign-categories/auto-populate', 'CampaignCategories::autoPopulate', ['filter' => 'adops']);
$routes->post('campaign-categories/add-selected', 'CampaignCategories::addSelected', ['filter' => 'adops']);
$routes->post('campaign-categories/apply-to-campaigns', 'CampaignCategories::applyToCampaigns', ['filter' => 'adops']);
$routes->get('campaign-categories/campaign/(:num)', 'CampaignCategories::getCampaignCategories/$1', ['filter' => 'adops']);
$routes->post('campaign-categories/campaign/(:num)', 'CampaignCategories::setCampaignCategories/$1', ['filter' => 'adops']);

// Ad Groups — gated on Advertiser_info.enable_adgroup by the `adgroup` filter.
// Legacy only hid the nav item and left `/ad-groups` reachable by any
// advertiser; the filter closes that. Static segments are declared before
// `(:num)` so `create` and `export` can't be read as an id.
$routes->get('ad-groups', 'AdGroups::index', ['filter' => 'adgroup']);
$routes->get('ad-groups/json', 'AdGroups::json', ['filter' => 'adgroup']);
$routes->get('ad-groups/create', 'AdGroups::create', ['filter' => 'adgroup']);
$routes->post('ad-groups/create', 'AdGroups::store', ['filter' => 'adgroup']);
$routes->get('ad-groups/export', 'AdGroups::export', ['filter' => 'adgroup']);
$routes->post('ad-groups/bulk-delete', 'AdGroups::bulkDelete', ['filter' => 'adgroup']);
// POST, not legacy's GET link — a prefetcher could fire that.
$routes->post('ad-groups/delete/(:num)', 'AdGroups::delete/$1', ['filter' => 'adgroup']);
$routes->get('ad-groups/(:num)/edit', 'AdGroups::edit/$1', ['filter' => 'adgroup']);
$routes->post('ad-groups/(:num)/edit', 'AdGroups::update/$1', ['filter' => 'adgroup']);

// Preview Tool — single-advertiser (15618); controller gates auth_can_preview_tool() with 404.
$routes->match(['GET', 'POST'], 'preview-tool', 'PreviewTool::index', ['filter' => 'auth']);
$routes->get('preview-tool/preview/(:num)', 'PreviewTool::preview/$1', ['filter' => 'auth']);

/**
 * Archive (soft-deleted entities) — Campaigns, Creatives, Keyword Lists, Keywords.
 * Ported from legacy advertisers7_new adcenter/controllers/archive/*.
 * AP-121: available to all advertisers (auth filter), matching legacy AdCenter where
 * Archive lived in the account dropdown with no role gate. ArchiveService scopes every
 * query and restore to the session advertiser, so an advertiser only sees their own rows.
 */
$routes->get('archive', 'Archive::index', ['filter' => 'auth']);
$routes->post('archive/restore-campaigns',    'Archive::restoreCampaigns',   ['filter' => 'auth']);
$routes->post('archive/restore-creatives',    'Archive::restoreCreatives',   ['filter' => 'auth']);
$routes->post('archive/restore-keyword-lists', 'Archive::restoreKeywordLists', ['filter' => 'auth']);
$routes->post('archive/restore-keywords',      'Archive::restoreKeywords',     ['filter' => 'auth']);

/**
 * Api Routes
 */
$routes->group('api', ['filter' => 'apikey'], function ($routes) {

    $routes->resource('campaigns', [
        'controller' => 'Api\CampaignController'
    ]);

});

/*
 * --------------------------------------------------------------------
 * Additional Routing
 * --------------------------------------------------------------------
 *
 * There will often be times that you need additional routing and you
 * need it to be able to override any defaults in this file. Environment
 * based routes is one such time. require() additional route files here
 * to make that happen.
 *
 * You will have access to the $routes object within that file without
 * needing to reload it.
 */

if (file_exists(APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php')) {
    require APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php';
}

