import os, json, zipfile, shutil, textwrap, re

root = 'git-search-tool-max-coverage'
if os.path.exists(root):
    shutil.rmtree(root)
for d in ['docs', 'index', 'tool/web/assets']:
    os.makedirs(os.path.join(root, d), exist_ok=True)

topics = {
    'quickstart': {'title': 'Most-Used Workflows', 'description': 'Everyday pull, edit, commit, release, and safety-check recipes.', 'order': 1},
    'setup': {'title': 'Setup & SSH', 'description': 'SSH, clone URLs, bare repositories, working copies, and folder layout.', 'order': 2},
    'remote': {'title': 'Remote & Sync', 'description': 'Remote URLs, clone, fetch, pull, push, and troubleshooting.', 'order': 3},
    'workflow': {'title': 'Workflows', 'description': 'Project creation, macOS/Linux development, releases, and fix copies.', 'order': 4},
    'versions': {'title': 'Versions & Tags', 'description': 'VERSION file, release tags, bumps, and version inspection.', 'order': 5},
    'branches': {'title': 'Branches', 'description': 'Listing, current/upstream branches, feature work, and maintenance lines.', 'order': 6},
    'files': {'title': 'Files & History', 'description': 'Status, add, ignore, list, compare, restore, export, dates, and sizes.', 'order': 7},
}

def item(id, typ, topic, title, question, tags, content, source='Session Q&A'):
    return {'id': id, 'type': typ, 'topic': topic, 'title': title, 'question': question,
            'tags': tags, 'content_md': content, 'source': source}

items = [
    item('quick_start_work', 'tutorial', 'quickstart', 'Start a normal work session',
         'What commands should I run before editing?',
         ['daily', 'pull', 'main', 'status', 'workflow'],
         'From a working copy:\n\n```bash\ngit status\ngit checkout main\ngit pull\n```\n\nResolve, commit, stash, or discard uncommitted work before pulling if Git reports a conflict or refuses to merge.'),
    item('quick_commit_push', 'tutorial', 'quickstart', 'Commit and push normal changes',
         'What is the standard edit, commit, and push sequence?',
         ['daily', 'add', 'commit', 'push', 'workflow'],
         'After editing and testing:\n\n```bash\ngit status\ngit add -A\ngit commit -m "Describe the change"\ngit push\n```\n\nUse `git add file1 file2` instead of `git add -A` when you only want selected files in the commit.'),
    item('quick_release', 'tutorial', 'quickstart', 'Release a tested version',
         'What is the short recipe for a new release?',
         ['release', 'VERSION', 'tag', 'bump', 'push'],
         'After release changes are committed and tested:\n\n```bash\n./git-bump-version 3.0.0\ngit push origin main\ngit push origin --tags\n```\n\nThe helper updates `VERSION`, commits it, and creates tag `v3.0.0`. The two pushes publish the branch and tag.'),
    item('quick_check', 'cheat', 'quickstart', 'Quick safety check before pull or push',
         'What should I check before synchronizing?',
         ['status', 'branch', 'upstream', 'safety', 'pull', 'push'],
         '```bash\ngit status\ngit branch --show-current\ngit rev-parse --abbrev-ref @{u}\n```\n\nThis shows uncommitted work, the current branch, and its tracking branch.'),
    item('setup_ssh', 'task', 'setup', 'Confirm SSH access to the Git server',
         'How do I confirm SSH works before using Git?',
         ['ssh', 'server', 'port 2222', 'key', 'connection'],
         'Run from macOS or another Linux client:\n\n```bash\nssh -p 2222 intel145@209.182.212.74 \'echo SSH_OK\'\n```\n\nIf it prints `SSH_OK`, SSH connectivity works. Git over SSH will not work until this succeeds.', 'tasks.md'),
    item('setup_clone', 'tutorial', 'setup', 'Clone current project from the SSH server',
         'How do I get the current version of a project from the remote repository?',
         ['clone', 'ssh', 'remote', 'current version', 'macos', 'linux'],
         'Use the `ssh://` URL form so the custom port is included:\n\n```bash\nmkdir -p ~/projects\ncd ~/projects\ngit clone ssh://intel145@209.182.212.74:2222/home/intel145/websites/gitrepo/proj1.git proj1\ncd proj1\n```\n\nVerify:\n\n```bash\ngit remote -v\ngit branch -a\ncat VERSION\ngit describe --tags --always\n```', 'faq4.md'),
    item('remote_origin_meaning', 'glossary', 'remote', 'Meaning of git remote add origin',
         'What does git remote add origin mean?',
         ['origin', 'remote add', 'alias', 'url'],
         'The command stores a named connection from the local working copy to another repository:\n\n```bash\ngit remote add origin <url>\n```\n\n`origin` is a conventional nickname. `git push origin main` means push local `main` to the repository named `origin`.'),
    item('remote_add_syntax', 'faq', 'remote', 'Correct SSH remote syntax with port 2222',
         'What was wrong with git remote add origin ssh -p 2222 user@host -i key?',
         ['remote add', 'ssh', 'port', '2222', 'identity file', 'url'],
         '`git remote add` accepts a remote name and one URL. SSH options such as `-p` and `-i` are not separate Git remote arguments. Use:\n\n```bash\ngit remote add origin ssh://intel145@209.182.212.74:2222/home/intel145/websites/gitrepo/proj1.git\n```\n\nConfigure the identity file in `~/.ssh/config` or use `GIT_SSH_COMMAND` when needed.'),
    item('branches_list', 'cheat', 'branches', 'List local, remote, and all branches',
         'How do I list branches?',
         ['branch', 'list', 'local', 'remote', 'all'],
         '```bash\ngit branch       # local branches\ngit branch -r    # remote-tracking branches\ngit branch -a    # local and remote-tracking branches\n```\n\nUse `git fetch origin --prune` first if you need fresh remote branch information.'),
    item('branches_current', 'cheat', 'branches', 'Show the current branch',
         'How do I check which branch I am currently on?',
         ['current branch', 'show current', 'HEAD'],
         '```bash\ngit branch --show-current\n```\n\nAlternatives: `git branch`, `git rev-parse --abbrev-ref HEAD`, or `git status`. `git branch` marks the current branch with `*`.'),
    item('versions_tags', 'faq', 'versions', 'When to use tags',
         'When should I use tags?',
         ['tag', 'release', 'version', 'milestone', 'v1', 'v2'],
         'Use tags to mark fixed release or milestone commits such as `v1.0.0`, `v2.0.0`, and `v2.1.0`. A branch is for ongoing development; a tag is a named snapshot that should normally remain unchanged.\n\n```bash\ngit tag -a v2.0.0 -m \'Release v2.0.0\'\ngit push origin --tags\n```', 'faq1.md'),
    item('versions_release', 'tutorial', 'versions', 'Make changes and release v3.0.0',
         'How do I get current files, make changes, make v3.0.0, and update the server?',
         ['release', 'v3.0.0', 'VERSION', 'tag', 'push', 'workflow'],
         '```bash\ngit checkout main\ngit pull\n# edit and test files\ngit add -A\ngit commit -m \'Implement v3 features\'\n./git-bump-version 3.0.0\ngit push origin main\ngit push origin --tags\n```\n\nThe script updates `VERSION`, commits it, and creates `v3.0.0`.', 'faq4.md'),
    item('files_status', 'cheat', 'files', 'List edited, staged, and untracked files',
         'How do I see files being edited or checked out?',
         ['status', 'modified', 'staged', 'untracked', 'files'],
         '```bash\ngit status\n```\n\nIt groups files into staged changes, unstaged changes, and untracked files. Use it before committing, pulling, or pushing.'),
    item('files_diff', 'cheat', 'files', 'Compare README.md between v1 and v2',
         'How do I compare README.md in v1 versus v2?',
         ['diff', 'README.md', 'v1', 'v2', 'compare'],
         '```bash\ngit --no-pager diff v1 v2 -- README.md\n```\n\nUse full tag names such as `v1.0.0` and `v2.0.0` if that is how your tags are named.'),
    item('files_get_old', 'cheat', 'files', 'View or save README.md from v1',
         'How do I get v1 of README.md without changing current files?',
         ['show', 'old version', 'README.md', 'tag', 'export'],
         'View:\n\n```bash\ngit show v1:README.md\n```\n\nSave separately:\n\n```bash\ngit show v1:README.md > README.v1.md\n```\n\nThis does not alter the current working copy.'),
]

items.sort(key=lambda x: (topics[x['topic']]['order'], x['title'].lower()))

with open(os.path.join(root, 'index', 'topics.json'), 'w') as f:
    json.dump(topics, f, indent=2)
with open(os.path.join(root, 'index', 'items.json'), 'w') as f:
    json.dump(items, f, indent=2)

docs = {
    'README.md': '# Git Search Tool – Maximum Coverage\n\nDependency-free PHP + CLI documentation browser for the Git Q&A, cheat sheets, troubleshooting, and workflows from this session.\n\n## Quick start\n\n```bash\ncd git-search-tool-max-coverage\nphp tool/cli.php topics\nphp tool/cli.php search "remote ssh"\nphp tool/cli.php show quick_release\nphp -S 127.0.0.1:8080 -t tool/web\n```\n\nOpen `http://127.0.0.1:8080/git.index.php`.\n\nThe index contains ' + str(len(items)) + ' organized entries.\n',
    'git.search.design.md': '# Git Search Tool Design\n\n## Goal\n\nBrowse, search, and maintain Git documentation in a reusable PHP/CLI tool.\n\n## Coverage\n\nThe data includes maximum coverage of the session Q&A plus a first topic, **Most-Used Workflows**, for daily commands. Topics are ordered: quickstart, setup, remote, workflow, versions, branches, files.\n',
    'docs/faq1.md': '# FAQ 1\n\nTags, branching strategy, version checking, file sizes/dates, and VERSION-file guidance. See `index/items.json` for organized answers.\n',
    'docs/faq2.md': '# FAQ 2\n\nmacOS/Linux working-copy scenarios, release workflow, and older-version testing.\n',
    'docs/faq3.md': '# FAQ 3\n\nWhen to branch and how to determine file/version history.\n',
    'docs/faq4.md': '# FAQ 4\n\nClone, modify, release v3.0.0, and push to the remote server.\n',
    'docs/faq5.md': '# FAQ 5\n\nSelective staging, .gitignore, and untracking files/folders.\n',
    'docs/tasks.md': '# Tasks\n\nGetting started, glossary, SSH/remote/repository-folder checks, and common workflows.\n',
    'docs/git1.md': '# Git Commands\n\nCore commands for files, history, version tags, commits, and remotes.\n',
    'docs/git.cheatsheet.md': '# Git Cheat Sheet\n\nTwo dev environments (macOS/Linux), one Linux bare repo, and proj1 example recipes.\n',
}

for name, content in docs.items():
    path = os.path.join(root, name)
    os.makedirs(os.path.dirname(path), exist_ok=True)
    with open(path, 'w') as f:
        f.write(content)

cli = r'''<?php
declare(strict_types=1);
$root = dirname(__DIR__);
function fail(string $message, int $code=1): never { fwrite(STDERR,"Error: $message\n"); exit($code); }
function load_json(string $path): array { if(!is_readable($path)) fail("Cannot read $path"); $x=json_decode((string)file_get_contents($path),true); if(!is_array($x)) fail("Invalid JSON: $path"); return $x; }
function usage(): void { echo "Git Search Tool CLI\n\nUsage:\n  php tool/cli.php help\n  php tool/cli.php topics\n  php tool/cli.php list [--topic=TOPIC] [--type=TYPE]\n  php tool/cli.php search \"words to find\" [--topic=TOPIC] [--type=TYPE]\n  php tool/cli.php show ITEM_ID\n\nExamples:\n  php tool/cli.php topics\n  php tool/cli.php list --topic=quickstart\n  php tool/cli.php search \"remote ssh\"\n  php tool/cli.php show quick_release\n"; }
function opt(array $args,string $key): ?string { $p="--$key="; foreach($args as $a) if(str_starts_with($a,$p)) return substr($a,strlen($p)); return null; }
function text(array $i): string { return strtolower(implode(' ',[$i['id']??'', $i['title']??'', $i['question']??'',implode(' ',$i['tags']??[]),$i['content_md']??'',$i['source']??''])); }
$items=load_json($root.'/index/items.json');$topics=load_json($root.'/index/topics.json');$args=$argv;array_shift($args);$cmd=$args[0]??'help';if($args)array_shift($args);
if(in_array($cmd,['help','--help','-h'],true)){usage();exit;}
if($cmd==='topics'){uasort($topics,fn($a,$b)=>($a['order']??999)<=>($b['order']??999));foreach($topics as $id=>$t)echo sprintf("%-14s %s\n  %s\n",$id,$t['title'],$t['description']);exit;}
if($cmd==='show'){ $id=$args[0]??'';foreach($items as $i)if($i['id']===$id){echo '# '.$i['title']."\n\n";if($i['question'])echo '**Question:** '.$i['question']."\n\n";echo $i['content_md']."\n\n".'_Topic: '.$i['topic'].' | Type: '.$i['type'].' | Source: '.$i['source']."_\n";exit;}fail("Item not found: $id"); }
if($cmd==='list'||$cmd==='search'){ $topic=opt($args,'topic');$type=opt($args,'type');$terms='';if($cmd==='search'){ $raw=array_values(array_filter($args,fn($a)=>!str_starts_with($a,'--')));$terms=strtolower(trim(implode(' ',$raw)));if($terms==='')fail('Search requires words.');}$n=0;foreach($items as $i){if($topic!==null&&$i['topic']!==$topic)continue;if($type!==null&&$i['type']!==$type)continue;if($terms!==''){ $hay=text($i);$ok=true;foreach(preg_split('/\s+/',$terms)as$w)if($w!==''&&!str_contains($hay,$w))$ok=false;if(!$ok)continue;}$n++;echo sprintf("%-26s %-10s %-14s %s\n",$i['id'],$i['type'],$i['topic'],$i['title']);}echo "\n$n item(s).\n";exit;}
fail("Unknown command: $cmd. Run: php tool/cli.php help");
'''

with open(os.path.join(root, 'tool', 'cli.php'), 'w') as f:
    f.write(cli)

web = r'''<?php
declare(strict_types=1);
$base=dirname(__DIR__,2);
function dat(string $p):array{if(!is_readable($p))throw new RuntimeException("Cannot read $p");$d=json_decode((string)file_get_contents($p),true);if(!is_array($d))throw new RuntimeException("Invalid JSON $p");return$d;}
function e(string $s):string{return htmlspecialchars($s,ENT_QUOTES|ENT_SUBSTITUTE,'UTF-8');}
function md(string $s):string{$x=e($s);$x=preg_replace_callback('/```([\w-]*)\n(.*?)```/s',fn($m)=>'<pre><code class="language-'.$m[1].'">'.$m[2].'</code></pre>',$x);$x=preg_replace('/`([^`]+)`/','<code>$1</code>',$x);$x=preg_replace('/\*\*([^*]+)\*\*/','<strong>$1</strong>',$x);$parts=preg_split('/\n\n+/',trim($x));$o=[];foreach($parts as$p){if(str_starts_with($p,'<pre>')){$o[]=$p;continue;}$lines=explode("\n",$p);$list=!empty($lines);foreach($lines as$l)if(!preg_match('/^-\s+/', $l)){$list=false;break;}if($list){$o[]='<ul>'.implode('',array_map(fn($l)=>'<li>'.preg_replace('/^-\s+/','',$l).'</li>',$lines)).'</ul>';}else{$o[]='<p>'.nl2br($p).'</p>';}}return implode("\n",$o);}
try{$items=dat($base.'/index/items.json');$topics=dat($base.'/index/topics.json');uasort($topics,fn($a,$b)=>($a['order']??999)<=>($b['order']??999));usort($items,fn($a,$b)=>(($topics[$a['topic']]['order']??999)<=>($topics[$b['topic']]['order']??999))?:strcmp($a['title'],$b['title']));}catch(Throwable$ex){http_response_code(500);echo'<pre>'.e($ex->getMessage()).'</pre>';exit;}
?><!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Git Search Tool</title><link rel="stylesheet" href="assets/style.css"></head><body><main class="page"><header class="hero"><div><p class="eyebrow">PHP / CLI documentation browser</p><h1>Git Search Tool</h1><p class="subtitle">Maximum-coverage Git Q&A, workflows, troubleshooting, and command reference.</p></div><div class="count"><span id="visibleCount"><?=count($items)?></span> entries</div></header><section class="controls"><label class="search">Full search<input id="searchBox" type="search" placeholder="Try: remote ssh, README, version tags"></label><label>Topic<select id="topicFilter"><option value="">All topics</option><?php foreach($topics as$id=>$t):?><option value="<?=e($id)?>"><?=e($t['title'])?></option><?php endforeach;?></select></label><label>Type<select id="typeFilter"><option value="">All types</option><option value="faq">FAQ</option><option value="tutorial">Tutorial</option><option value="task">Task / check</option><option value="cheat">Cheat sheet</option><option value="glossary">Glossary</option></select></label><div class="buttons"><button id="expandAll" type="button">Expand all</button><button id="collapseAll" type="button">Collapse all</button><button id="clearFilters" class="secondary" type="button">Clear</button></div></section><p id="emptyMessage" class="empty" hidden>No matching entries.</p><section id="entries"><?php foreach($items as$i):$t=$topics[$i['topic']]??['title'=>$i['topic']];$search=strtolower(implode(' ',[$i['title'],$i['question'],implode(' ',$i['tags']),$i['content_md'],$i['source']]));?><details class="entry" id="<?=e($i['id'])?>" data-topic="<?=e($i['topic'])?>" data-type="<?=e($i['type'])?>" data-search="<?=e($search)?>"><summary><span class="pill"><?=e($t['title'])?></span><span class="pill type"><?=e($i['type'])?></span><strong><?=e($i['title'])?></strong><span class="question"><?=e($i['question'])?></span></summary><div class="body"><div class="markdown"><?=md($i['content_md'])?></div><footer>Source: <?=e($i['source'])?> · Tags: <?=e(implode(', ',$i['tags']))?> · <a href="#<?=e($i['id'])?>">Link</a></footer></div></details><?php endforeach;?></section></main><script src="assets/app.js"></script></body></html>'''

with open(os.path.join(root, 'tool', 'web', 'git.index.php'), 'w') as f:
    f.write(web)

js = r'''(()=>{const E=[...document.querySelectorAll('.entry')],q=document.querySelector('#searchBox'),t=document.querySelector('#topicFilter'),y=document.querySelector('#typeFilter'),n=document.querySelector('#visibleCount'),z=document.querySelector('#emptyMessage');const f=()=>{const w=q.value.toLowerCase().trim().split(/\s+/).filter(Boolean);let c=0;E.forEach(x=>{const ok=(!t.value||x.dataset.topic===t.value)&&(!y.value||x.dataset.type===y.value)&&w.every(a=>x.dataset.search.includes(a));x.hidden=!ok;if(ok)c++});n.textContent=c;z.hidden=c!==0};[q,t,y].forEach(x=>x.addEventListener('input',f));t.addEventListener('change',f);y.addEventListener('change',f);document.querySelector('#expandAll').onclick=()=>E.filter(x=>!x.hidden).forEach(x=>x.open=true);document.querySelector('#collapseAll').onclick=()=>E.forEach(x=>x.open=false);document.querySelector('#clearFilters').onclick=()=>{q.value='';t.value='';y.value='';f();q.focus()};if(location.hash){const x=document.querySelector(location.hash);if(x){x.open=true;setTimeout(()=>x.scrollIntoView(),0)}}})();'''

with open(os.path.join(root, 'tool', 'web', 'assets', 'app.js'), 'w') as f:
    f.write(js)

css = r''':root{--bg:#f3f6f8;--panel:#fff;--ink:#16212b;--muted:#5e6d79;--line:#d8e0e6;--accent:#0969da;--code:#16202a}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:16px/1.52 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.page{max-width:1120px;margin:0 auto;padding:28px 20px 64px}.hero{display:flex;justify-content:space-between;gap:24px;align-items:flex-start;margin:8px 0 24px}.eyebrow{text-transform:uppercase;letter-spacing:.09em;color:var(--accent);font-weight:700;font-size:.76rem;margin:0 0 4px}h1{margin:0;line-height:1.1;font-size:clamp(2rem,5vw,3rem)}.subtitle{color:var(--muted);margin:8px 0 0;max-width:700px}.count{white-space:nowrap;background:#e8f1fc;border:1px solid #c4dbf8;border-radius:999px;padding:7px 12px;color:#174f86;font-weight:700}.controls{display:grid;grid-template-columns:minmax(240px,2fr) minmax(140px,1fr) minmax(140px,1fr) auto;gap:12px;align-items:end;background:var(--panel);padding:16px;border:1px solid var(--line);border-radius:12px;box-shadow:0 8px 30px rgba(24,39,55,.08);position:sticky;top:0;z-index:2}label{display:grid;gap:5px;font-size:.82rem;font-weight:700;color:var(--muted)}input,select,button{font:inherit}input,select{border:1px solid #b8c6d1;border-radius:7px;min-height:39px;padding:8px 10px;color:var(--ink);background:#fff}.search-label input{width:100%}.button-row{display:flex;flex-wrap:wrap;gap:7px}button{border:1px solid var(--accent);background:var(--accent);color:white;border-radius:7px;padding:9px 11px;cursor:pointer;font-weight:650}button:hover{background:var(--accent-dark)}button.secondary{color:#21415e;background:#fff;border-color:#a8b8c5}#entries{margin-top:18px;display:grid;gap:10px}.entry{background:var(--panel);border:1px solid var(--line);border-radius:10px;box-shadow:0 2px 8px rgba(24,39,55,.035);scroll-margin-top:112px}.entry[hidden]{display:none}summary{cursor:pointer;padding:15px 18px;list-style:none}summary::-webkit-details-marker{display:none}summary::before{content:'›';color:var(--accent);display:inline-block;margin-right:9px;font-size:1.4rem;vertical-align:-2px;transition:transform .16s}details[open] summary::before{transform:rotate(90deg)}.summary-copy{display:inline}.topic-pill,.type-pill{display:inline-block;font-size:.7rem;font-weight:750;letter-spacing:.025em;border-radius:999px;padding:2px 7px;margin-right:6px;vertical-align:2px}.topic-pill{color:#165588;background:#e8f3fc}.type-pill{color:#5a4076;background:#f0eafa;text-transform:uppercase}.type-task{color:#6f4c0f;background:#fff1cf}.type-cheat{color:#12634f;background:#def8ed}.type-glossary{color:#5e4b0b;background:#fbf3cd}.entry-title{font-weight:760;font-size:1.03rem}.question{display:block;color:var(--muted);margin:4px 0 0 31px;font-size:.92rem}.entry-body{border-top:1px solid var(--line);padding:16px 19px 13px 48px}.markdown>:first-child{margin-top:0}.markdown>:last-child{margin-bottom:0}.markdown p{margin:0 0 12px}.markdown ul{margin:6px 0 12px;padding-left:22px}.markdown pre{background:var(--code);color:#e9f3fb;overflow:auto;padding:13px;border-radius:8px;line-height:1.42}.markdown code{font:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;background:#edf2f6;padding:1px 4px;border-radius:4px;font-size:.9em}.markdown pre code{background:none;padding:0}footer{border-top:1px solid #e7edf1;margin-top:15px;padding-top:10px;color:var(--muted);font-size:.78rem;display:flex;gap:15px;flex-wrap:wrap}footer a{color:var(--accent)}.empty{padding:20px;border-radius:10px;background:#fff7df;color:#775a0c}@media(max-width:780px){.hero{display:block}.count{display:inline-block;margin-top:14px}.controls{position:static;grid-template-columns:1fr 1fr}.search-label{grid-column:1/-1}.button-row{grid-column:1/-1}.entry-body{padding-left:18px}.question{margin-left:0}}@media(max-width:480px){.controls{grid-template-columns:1fr}.page{padding:18px 12px}}'''

with open(os.path.join(root, 'tool', 'web', 'assets', 'style.css'), 'w') as f:
    f.write(css)

zip_name = 'git-search-tool-max-coverage.zip'
with zipfile.ZipFile(zip_name, 'w', zipfile.ZIP_DEFLATED) as z:
    for current, dirs, files in os.walk(root):
        for fn in files:
            full = os.path.join(current, fn)
            z.write(full, os.path.relpath(full, '.'))

print('Created', zip_name)
