<?php

declare(strict_types=1);

namespace Atdt\Import;

/**
 * The source picker (SPEC-IMPORT §5): packages a SysOp recognizes,
 * formats for those who know what they have, and the ATDT interchange
 * format for everything else. Renegade and MBSE were specified with a
 * verify-before-shipping gate; neither could be confirmed against a
 * real install, so per the spec they are removed rather than guessed
 * at (their stores stay reachable through the By-format group).
 */
final class Sources
{
    /**
     * Group one, ordered by likelihood (§5.4). 'ask' lists the format
     * sub-choice a package needs; 'format' is fixed when there is one.
     * 'help' rows: [storage format, files needed, where they live, note].
     */
    public const PACKAGES = [
        'mystic' => [
            'title' => 'Mystic BBS',
            'ask' => ['jam' => 'JAM (.jhr/.jdt/.jdx files)', 'squish' => 'Squish (.sqd/.sqi files)'],
            'help' => ['JAM or Squish (Mystic is configurable between the two)',
                'One set of files per message base, sharing a filename stem',
                'The message base directory set in Mystic\'s configuration (often msgs/)',
                'Check the file extensions in your msgs directory to see which store you ran.'],
        ],
        'synchronet' => [
            'title' => 'Synchronet',
            'format' => 'smb',
            'help' => ['Synchronet Message Base (SMB)',
                'Sets of .shd/.sdt/.sid files sharing an internal code',
                'data/subs/ under the Synchronet root',
                'Sub-board display names live in the board config, so areas arrive named by filename stem.'],
        ],
        'enigma' => [
            'title' => 'ENiGMA 1/2',
            'format' => 'enigma',
            'help' => ['SQLite database',
                'The single file message.sqlite3',
                'db/message.sqlite3 under the ENiGMA root',
                'The easiest source of all: it is a real database. Private mail is excluded automatically.'],
        ],
        'talisman' => [
            'title' => 'Talisman BBS',
            'format' => 'squish',
            'help' => ['Squish message base',
                'One .sqd and .sqi pair per area, sharing a filename stem',
                'The message base directory from talisman.ini',
                ''],
        ],
        'magicka' => [
            'title' => 'Magicka BBS',
            'format' => 'jam',
            'help' => ['JAM message base',
                'One .jhr/.jdt/.jdx set per area, sharing a filename stem',
                'The message base directory from the Magicka config',
                ''],
        ],
        'remoteaccess' => [
            'title' => 'RemoteAccess',
            'ask' => ['jam' => 'JAM (.jhr/.jdt/.jdx files)', 'hudson' => 'Hudson (MSGHDR.BBS and friends)'],
            'help' => ['JAM (later versions) or the Hudson base (earlier ones)',
                'JAM: file sets per area. Hudson: one shared MSGHDR.BBS/MSGTXT.BBS/MSGIDX.BBS/MSGINFO.BBS set',
                'The message base path from RACONFIG',
                'If your MSGS directory holds MSGHDR.BBS, you have a Hudson base.'],
        ],
        'telegard' => [
            'title' => 'Telegard',
            'ask' => ['hudson' => 'Hudson (MSGHDR.BBS and friends)', 'jam' => 'JAM (.jhr/.jdt/.jdx files)'],
            'help' => ['Hudson (most versions) or JAM (later ones)',
                'Hudson: one shared MSGHDR.BBS/MSGTXT.BBS/MSGIDX.BBS/MSGINFO.BBS set. JAM: file sets per area',
                'The message base path from the Telegard config',
                ''],
        ],
        'quickbbs' => [
            'title' => 'QuickBBS',
            'format' => 'hudson',
            'help' => ['The original Hudson message base',
                'MSGHDR.BBS, MSGTXT.BBS, MSGIDX.BBS, MSGINFO.BBS in one directory',
                'The MSGS directory of the QuickBBS install',
                'Areas are numbered, not named; the mapping screen is where you name them.'],
        ],
        'maximus' => [
            'title' => 'Maximus',
            'format' => 'squish',
            'help' => ['Squish message base (Squish originated with Maximus)',
                'One .sqd and .sqi pair per area, sharing a filename stem',
                'The message area paths from the Maximus config',
                ''],
        ],
        'elebbs' => [
            'title' => 'EleBBS',
            'format' => 'jam',
            'help' => ['JAM message base',
                'One .jhr/.jdt/.jdx set per area, sharing a filename stem',
                'The message base path from EleConfig',
                ''],
        ],
        'proboard' => [
            'title' => 'ProBoard',
            'format' => 'jam',
            'help' => ['JAM message base',
                'One .jhr/.jdt/.jdx set per area, sharing a filename stem',
                'The message base path from ProConfig',
                ''],
        ],
        'wwiv' => [
            'title' => 'WWIV',
            'format' => 'qwk',
            'help' => ['Not supported directly: WWIV uses a proprietary store',
                'A QWK packet (.qwk zip) generated from your WWIV board',
                'Log into the board with a QWK-capable reader, or use its offline mail door, and download the packet',
                'Every conference in the packet arrives named, so the mapping screen gets real area names.'],
        ],
    ];

    /** Group two: for sysops who know what they have (§5.2). */
    public const FORMATS = [
        'jam' => ['title' => 'JAM message base',
            'help' => ['JAM', 'Sets of .jhr/.jdt/.jdx files, one set per area',
                'Wherever the board kept its message base', '']],
        'squish' => ['title' => 'Squish message base',
            'help' => ['Squish', 'Pairs of .sqd/.sqi files, one pair per area',
                'Wherever the board kept its message base', '']],
        'smb' => ['title' => 'Synchronet SMB message base',
            'help' => ['SMB', 'Sets of .shd/.sdt/.sid files', 'data/subs/ on a live Synchronet board', '']],
        'hudson' => ['title' => 'Hudson message base',
            'help' => ['Hudson', 'MSGHDR.BBS, MSGTXT.BBS, MSGIDX.BBS, MSGINFO.BBS in one directory',
                'The MSGS directory of a QuickBBS-lineage board',
                'Areas are numbered, not named; the mapping screen is where you name them.']],
        'fts1' => ['title' => 'FTS-1 *.msg directory',
            'help' => ['FTS-0001 stored messages', 'Numbered .msg files, one per message, one directory per area',
                'Echo or netmail directories of an FTN mailer',
                'Mostly used for netmail, which is out of scope; point it at echo directories.']],
        'qwk' => ['title' => 'QWK packet',
            'help' => ['QWK offline mail packet', 'One .qwk file (a zip holding CONTROL.DAT and MESSAGES.DAT)',
                'Made by any QWK door or reader; nearly every BBS ever written can produce one',
                'Subjects are capped at 25 characters in the classic format and threading is weak: a QWK import is a content rescue, not a faithful copy.']],
        'enigma' => ['title' => 'ENiGMA 1/2 SQLite database',
            'help' => ['SQLite', 'The single file message.sqlite3', 'db/ under the ENiGMA root', '']],
    ];

    /** Group three, permanent (§5.3). */
    public const OTHER = [
        'atdt' => ['title' => 'ATDT Import Format (.zip)',
            'help' => ['ATDT\'s own documented JSONL interchange format',
                'A .zip holding manifest.json and one .jsonl file per area',
                'Written by you, or by any exporter following the published format',
                'This is the entry that covers C64, Apple II, Atari, and every package ATDT will never parse natively. The format documentation ships in docs/ and on the website.']],
    ];

    /** Resolve a parser key to a Source instance. */
    public static function make(string $format): ?Source
    {
        return match ($format) {
            'atdt' => new Sources\Jsonl(),
            'jam' => new Sources\Jam(),
            'squish' => new Sources\Squish(),
            'smb' => new Sources\Smb(),
            'hudson' => new Sources\Hudson(),
            'fts1' => new Sources\Fts1(),
            'qwk' => new Sources\Qwk(),
            'enigma' => new Sources\Enigma(),
            default => null,
        };
    }

    /** Default source encoding per format (§14.1). */
    public static function defaultEncoding(string $format): string
    {
        return match ($format) {
            'enigma', 'atdt' => 'utf-8',
            default => 'cp437',
        };
    }

    /** The picker entry (any group) for a package/format key. */
    public static function entry(string $key): ?array
    {
        return self::PACKAGES[$key] ?? self::FORMATS[$key] ?? self::OTHER[$key] ?? null;
    }

    /** The parser key a picker choice resolves to (null: needs the sub-choice). */
    public static function resolve(string $key, string $sub = ''): ?string
    {
        if (isset(self::FORMATS[$key])) {
            return $key;
        }
        if (isset(self::OTHER[$key])) {
            return 'atdt';
        }
        $p = self::PACKAGES[$key] ?? null;
        if ($p === null) {
            return null;
        }
        if (isset($p['format'])) {
            return (string)$p['format'];
        }
        return isset($p['ask'][$sub]) ? $sub : null;
    }
}
