"""Five intentionally weak local CTF challenges and a separate fixed comparison.

Never deploy this teaching app as a public service. The fixed identity is a
server-side lab fixture, NOT a production login implementation.
"""
import argparse
import threading

from flask import Flask, jsonify, make_response, request
from markupsafe import escape

FLAGS = {
    1: "FLAG{v1ew_s0urce_1s_th3_f1rst_t00l}",
    2: "FLAG{r0b0ts_txt_1s_n0t_a_d00r}",
    3: "FLAG{r34d_th3_h34d3rs_c4r3fully}",
    4: "FLAG{c00k13s_c4n_b3_c00k3d}",
    5: "FLAG{1d0r_ch4ng3_th3_numb3r}",
}


def create_app(mode="challenge", role="user", language="ko"):
    if mode not in {"challenge", "fixed"} or role not in {"user", "admin"} or language not in {"ko", "en"}:
        raise ValueError("Unknown lab configuration")
    app = Flask(__name__)
    app.config.update(MAX_CONTENT_LENGTH=4096, TRUSTED_HOSTS=["127.0.0.1", "localhost"])
    solved = set()
    lock = threading.Lock()
    korean = language == "ko"

    def text(ko, en):
        return ko if korean else en

    def page(title, body):
        return f'''<!doctype html><html lang="{language}"><meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"><title>{escape(title)}</title>
<style>body{{font-family:system-ui,sans-serif;max-width:760px;margin:32px auto;padding:0 20px;line-height:1.7}}input,select,button{{font:inherit;padding:6px}}input{{max-width:90%}}code{{overflow-wrap:anywhere}}</style>
<h1>{escape(title)}</h1><p>{text('로컬 학습 실습','Local learning exercise')} · {text('취약 모드','Challenge mode') if mode=='challenge' else text('수정 비교 모드','Fixed comparison')}</p>{body}
<p><a href="/">{text('대회장으로','Back to arena')}</a></p></html>'''

    @app.after_request
    def no_cache(response):
        response.headers['Cache-Control'] = 'no-store'
        return response

    @app.get('/')
    def index():
        with lock:
            count = len(solved)
        comment = f'<!-- First flag / 첫 플래그: {FLAGS[1]} -->' if mode == 'challenge' else ''
        form = f'''<h2>{text('플래그 제출','Submit a flag')}</h2>
<form action="/submit" method="post"><label>{text('문제 번호','Challenge number')} <select name="challenge">{''.join(f'<option>{n}</option>' for n in FLAGS)}</select></label><br>
<label>{text('찾은 플래그','Found flag')} <input name="flag" required maxlength="150" autocomplete="off"></label><br>
<button type="submit">{text('제출','Submit')}</button></form>''' if mode == 'challenge' else ''
        body = f'''<p>{text('다섯 플래그를 찾고 관찰 근거를 기록하세요.','Find five flags and record the evidence for each.')}</p>
<ul><li><a href="/robots.txt">robots.txt</a></li><li><a href="/notice">{text('공지사항','Notice')}</a></li>
<li><a href="/enter">{text('입장','Entrance')}</a></li><li><a href="/profile?uid=2">{text('내 프로필','My profile')}</a></li></ul>
<p>{text('정답 처리','Accepted')}: {count}/5 · <a href="/score">{text('점수 JSON','Score JSON')}</a></p>{form}{comment}'''
        return page(text('미니 CTF 대회장','Mini CTF Arena'), body)

    @app.get('/robots.txt')
    def robots():
        return 'User-agent: *\nDisallow: /secret_backup\n', 200, {'Content-Type':'text/plain; charset=utf-8'}

    @app.get('/secret_backup')
    def backup():
        if mode == 'fixed' and role != 'admin':
            return page(text('접근 거부','Access denied'), '<p>403</p>'), 403
        return page(text('백업','Backup'), f'<p>{FLAGS[2]}</p>')

    @app.get('/notice')
    def notice():
        response = make_response(page(text('공지사항','Notice'), '<p>'+text('이 응답을 관찰하세요.','Inspect this response.')+'</p>'))
        if mode == 'challenge':
            response.headers['X-Flag'] = FLAGS[3]
        return response

    @app.get('/enter')
    def enter():
        allowed = request.cookies.get('admin') == 'true' if mode == 'challenge' else role == 'admin'
        if allowed:
            return page(text('관리자 입장','Admin entrance'), f'<p>{FLAGS[4]}</p>')
        response = make_response(page(text('일반 사용자','Regular user'), '<p>'+text('관리자만 이 플래그를 볼 수 있습니다.','Only admins can see this flag.')+'</p>'), 200 if mode=='challenge' else 403)
        if mode == 'challenge':
            response.set_cookie('admin', 'false', samesite='Lax')
        return response

    @app.get('/profile')
    def profile():
        uid = request.args.get('uid', '2')
        if uid not in {'1','2'}:
            return page(text('잘못된 사용자 번호','Invalid user ID'), '<p>'+text('사용자 번호 1 또는 2를 입력하세요.','Use user ID 1 or 2.')+'</p>'), 400
        if mode == 'fixed' and role != 'admin' and uid != '2':
            return page(text('접근 거부','Access denied'), '<p>403</p>'), 403
        content = FLAGS[5] if uid=='1' else 'user2'
        return page(text('프로필','Profile'), f'<p>uid={escape(uid)} · {content}</p>')

    @app.get('/score')
    def score():
        with lock:
            return jsonify(solved=sorted(solved), accepted=len(solved), total=5, persistent=False)

    @app.post('/submit')
    def submit():
        if mode != 'challenge':
            return page(text('비교 모드','Comparison mode'), '<p>'+text('수정 비교 모드에서는 제출하지 않습니다.','Submission is disabled in comparison mode.')+'</p>'), 403
        number = request.form.get('challenge', '')
        flag = request.form.get('flag', '')
        if number not in {'1','2','3','4','5'}:
            return page(text('잘못된 문제 번호','Invalid challenge number'), ''), 400
        accepted = flag == FLAGS[int(number)]
        with lock:
            duplicate = int(number) in solved
            if accepted:
                solved.add(int(number))
            count = len(solved)
        result = text('정답','Correct') if accepted else text('오답: 문제 번호와 전체 문자열을 확인하세요.','Incorrect: check the challenge number and complete string.')
        if accepted and duplicate:
            result += text(' · 이미 정답 처리됨',' · Already accepted')
        return page(text('제출 결과','Submission result'), f'<p>{result}</p><p>{count}/5</p>')

    return app


def port_number(value):
    port = int(value)
    if not 1024 <= port <= 65535:
        raise argparse.ArgumentTypeError('Choose a port from 1024 to 65535')
    return port


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--mode',choices=['challenge','fixed'],default='challenge')
    parser.add_argument('--role',choices=['user','admin'],default='user',help='Server-side fixture for fixed mode; not a login')
    parser.add_argument('--lang',choices=['ko','en'],default='ko')
    parser.add_argument('--port',type=port_number,default=8176)
    args = parser.parse_args()
    create_app(args.mode,args.role,args.lang).run(host='127.0.0.1',port=args.port,debug=False,use_reloader=False)


if __name__=='__main__':
    main()
