from pathlib import Path

from docx import Document
from docx.enum.section import WD_SECTION
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT, WD_CELL_VERTICAL_ALIGNMENT
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.shared import Cm, Pt, RGBColor


OUT = Path(r"C:\Users\sdfgjnfjhkgbjkhdfg\Documents\autoprokat\Отчет_ПМ11_Автопрокат.docx")


def set_cell_shading(cell, fill):
    tc_pr = cell._tc.get_or_add_tcPr()
    shd = OxmlElement("w:shd")
    shd.set(qn("w:fill"), fill)
    tc_pr.append(shd)


def set_cell_text(cell, text, bold=False):
    cell.text = ""
    p = cell.paragraphs[0]
    run = p.add_run(str(text))
    run.bold = bold
    run.font.name = "Times New Roman"
    run.font.size = Pt(12)
    cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER


def add_table(doc, title, headers, rows):
    p = doc.add_paragraph(title)
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    p.runs[0].font.name = "Times New Roman"
    p.runs[0].font.size = Pt(12)

    table = doc.add_table(rows=1, cols=len(headers))
    table.alignment = WD_TABLE_ALIGNMENT.CENTER
    table.style = "Table Grid"
    for i, h in enumerate(headers):
        set_cell_text(table.rows[0].cells[i], h, True)
        set_cell_shading(table.rows[0].cells[i], "D9EAF7")
    for row in rows:
        cells = table.add_row().cells
        for i, value in enumerate(row):
            set_cell_text(cells[i], value)
    doc.add_paragraph()
    return table


def add_heading(doc, text, level=1):
    p = doc.add_heading(text, level=level)
    for run in p.runs:
        run.font.name = "Times New Roman"
        run.font.color.rgb = RGBColor(0, 0, 0)
    return p


def add_paragraph(doc, text="", first_line=True):
    p = doc.add_paragraph()
    p.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY
    p.paragraph_format.line_spacing = 1.5
    if first_line:
        p.paragraph_format.first_line_indent = Cm(1.25)
    run = p.add_run(text)
    run.font.name = "Times New Roman"
    run.font.size = Pt(14)
    return p


def add_bullets(doc, items):
    for item in items:
        p = doc.add_paragraph(style=None)
        p.paragraph_format.left_indent = Cm(1.25)
        p.paragraph_format.line_spacing = 1.5
        run = p.add_run(item)
        run.font.name = "Times New Roman"
        run.font.size = Pt(14)


def add_code(doc, text):
    p = doc.add_paragraph()
    p.paragraph_format.line_spacing = 1.0
    run = p.add_run(text)
    run.font.name = "Courier New"
    run.font.size = Pt(9)


def set_document_style(doc):
    section = doc.sections[0]
    section.top_margin = Cm(2)
    section.bottom_margin = Cm(2)
    section.left_margin = Cm(3)
    section.right_margin = Cm(1.5)

    styles = doc.styles
    styles["Normal"].font.name = "Times New Roman"
    styles["Normal"].font.size = Pt(14)
    for style_name in ["Heading 1", "Heading 2", "Heading 3"]:
        style = styles[style_name]
        style.font.name = "Times New Roman"
        style.font.color.rgb = RGBColor(0, 0, 0)


def title_page(doc):
    lines = [
        "Министерство науки и высшего образования Российской Федерации",
        "Федеральное государственное бюджетное образовательное учреждение высшего образования",
        "«Новгородский государственный университет имени Ярослава Мудрого»",
        "ИНСТИТУТ ЭЛЕКТРОННЫХ И ИНФОРМАЦИОННЫХ СИСТЕМ",
        "ПОЛИТЕХНИЧЕСКИЙ КОЛЛЕДЖ",
    ]
    for line in lines:
        p = doc.add_paragraph()
        p.alignment = WD_ALIGN_PARAGRAPH.CENTER
        r = p.add_run(line)
        r.font.name = "Times New Roman"
        r.font.size = Pt(12)
        if line.isupper():
            r.bold = True

    doc.add_paragraph("\n\n")
    p = doc.add_paragraph("Утверждаю:\n\nЗам. директора по УМ и ВР\n\n____________________\n\n«___» __________ 202_ г.")
    p.alignment = WD_ALIGN_PARAGRAPH.RIGHT

    doc.add_paragraph("\n\n")
    for line, size, bold in [
        ("ПРОИЗВОДСТВЕННАЯ ПРАКТИКА", 16, True),
        ("ПМ.11 РАЗРАБОТКА И АДМИНИСТРИРОВАНИЕ БАЗ ДАННЫХ", 14, True),
        ("09.02.07 Информационные системы и программирование", 12, False),
        ("код и наименование специальности", 10, False),
        ("ОТЧЁТ", 18, True),
        ("по теме: разработка базы данных и приложения «Автопрокат»", 14, True),
    ]:
        p = doc.add_paragraph()
        p.alignment = WD_ALIGN_PARAGRAPH.CENTER
        r = p.add_run(line)
        r.font.name = "Times New Roman"
        r.font.size = Pt(size)
        r.bold = bold

    doc.add_paragraph("\n\n")
    p = doc.add_paragraph("Руководитель от организации:\n____________________\n«___» __________ 202_ г.\n")
    p.alignment = WD_ALIGN_PARAGRAPH.LEFT
    p = doc.add_paragraph("Руководитель от колледжа:\n____________________\n«___» __________ 202_ г.\n")
    p.alignment = WD_ALIGN_PARAGRAPH.LEFT
    p = doc.add_paragraph("Выполнил:\nСтудент гр. ________\n____________________\n«___» __________ 202_ г.")
    p.alignment = WD_ALIGN_PARAGRAPH.RIGHT
    doc.add_page_break()


def add_contents(doc):
    add_heading(doc, "Содержание", 1)
    items = [
        "Постановка задачи на практику",
        "1 Инструкция по технике безопасности на рабочем месте",
        "2 Должностные обязанности специалиста",
        "3 Выполнение индивидуального задания",
        "3.1 Проектирование модели базы данных",
        "3.2 Словарь данных базы данных",
        "3.3 Создание объектов базы данных",
        "3.4 Руководство пользователя/администратора",
        "3.5 Методы защиты данных в базе данных",
        "4 Задание от предприятия",
        "Заключение",
        "Приложение А",
        "Приложение Б",
    ]
    for item in items:
        p = doc.add_paragraph()
        r = p.add_run(item)
        r.font.name = "Times New Roman"
        r.font.size = Pt(14)
    doc.add_page_break()


def main():
    doc = Document()
    set_document_style(doc)
    title_page(doc)
    add_contents(doc)

    add_heading(doc, "Постановка задачи на практику", 1)
    add_paragraph(doc, "Задачей данного задания по модулю является проектирование и разработка базы данных, а также графического приложения для работы с ней. В качестве предметной области выбрана система автопроката, предназначенная для учета марок автомобилей, автомобилей, клиентов и договоров аренды.")
    add_paragraph(doc, "Графическое приложение должно обеспечивать вход пользователя в систему, просмотр данных из базы данных, ввод новых записей, поиск, редактирование, удаление, а также формирование отчетных документов. Для разработки приложения выбрана технология Windows Forms на языке C#, а в качестве СУБД используется PostgreSQL.")
    add_paragraph(doc, "Так как проектируемая система является многопользовательской, были спроектированы роли пользователей: администратор, менеджер и пользователь просмотра. Для защиты данных реализованы ограничения целостности на уровне базы данных, параметризованные запросы в приложении, разграничение прав и журналирование операций.")
    add_paragraph(doc, "Задание по модулю ПМ.11 считается завершенным при выполнении следующих этапов работ:")
    add_bullets(doc, [
        "проектирование модели базы данных;",
        "разработка базы данных в СУБД PostgreSQL;",
        "разработка приложения для работы с базой данных;",
        "реализация методов защиты на уровне БД и приложения;",
        "оформление отчета о выполнении индивидуального задания.",
    ])

    add_heading(doc, "1 Инструкция по технике безопасности на рабочем месте", 1)
    add_paragraph(doc, "При работе за компьютером необходимо соблюдать требования техники безопасности, правила эксплуатации оборудования и меры защиты информации. Рабочее место должно быть организовано так, чтобы исключить повышенную нагрузку на зрение, опорно-двигательный аппарат и нервную систему.")
    add_bullets(doc, [
        "перед началом работы проверить исправность компьютера, монитора, клавиатуры и мыши;",
        "не работать с оборудованием при поврежденных кабелях и разъемах;",
        "соблюдать расстояние от глаз до монитора не менее 50-70 см;",
        "делать перерывы при длительной работе за компьютером;",
        "использовать сложные пароли и не передавать учетные данные другим лицам;",
        "не открывать подозрительные файлы и ссылки;",
        "регулярно сохранять результаты работы и создавать резервные копии;",
        "по окончании работы корректно завершать работу программ и выходить из учетной записи.",
    ])

    add_heading(doc, "2 Должностные обязанности специалиста", 1)
    add_paragraph(doc, "В рамках выполнения задания специалист осуществляет анализ предметной области, проектирование структуры базы данных, создание таблиц и связей, разработку пользовательского приложения, настройку доступа и документирование результата.")
    add_paragraph(doc, "К основным обязанностям специалиста при выполнении работы относятся: проектирование ER-модели, создание нормализованной реляционной схемы, разработка SQL-скриптов, заполнение базы тестовыми данными, создание приложения для ввода и обработки данных, реализация защиты информации и подготовка отчета.")

    add_heading(doc, "3 Выполнение индивидуального задания", 1)
    add_heading(doc, "3.1 Проектирование модели базы данных", 2)
    add_paragraph(doc, "В ходе выполнения индивидуального задания была спроектирована база данных «Автопрокат». База данных приведена к третьей нормальной форме. Основными сущностями являются: марка автомобиля, автомобиль, клиент и договор аренды.")
    add_paragraph(doc, "Связи между таблицами имеют следующий вид: одна марка может иметь несколько автомобилей; один автомобиль может участвовать в нескольких договорах аренды; один клиент может оформить несколько договоров аренды. Центральной таблицей предметной области является rental, связывающая клиента и автомобиль.")

    add_heading(doc, "3.2 Словарь данных базы данных", 2)
    add_paragraph(doc, "База данных состоит из следующих основных таблиц:")
    add_bullets(doc, [
        "brand - справочник марок автомобилей;",
        "car - автомобили автопарка;",
        "client - клиенты автопроката;",
        "rental - договоры аренды;",
        "app_user - учетные записи приложения;",
        "audit_log - журнал операций;",
        "app_context - служебный контекст текущего пользователя приложения.",
    ])

    add_table(doc, "Таблица 1 - Таблица brand", ["Название атрибута", "Тип данных", "Назначение", "Ключевое поле"], [
        ["brand_id", "SERIAL", "Уникальный идентификатор марки", "Primary key"],
        ["name", "VARCHAR(50)", "Название марки автомобиля", "UNIQUE"],
        ["country", "VARCHAR(50)", "Страна-производитель", "Нет"],
    ])
    add_table(doc, "Таблица 2 - Таблица car", ["Название атрибута", "Тип данных", "Назначение", "Ключевое поле"], [
        ["car_id", "SERIAL", "Уникальный идентификатор автомобиля", "Primary key"],
        ["license_plate", "VARCHAR(15)", "Государственный номер", "UNIQUE"],
        ["brand_id", "INT", "Ссылка на марку автомобиля", "Foreign key"],
        ["year", "SMALLINT", "Год выпуска автомобиля", "CHECK"],
        ["daily_rate", "NUMERIC(10,2)", "Стоимость аренды за день", "CHECK"],
        ["status", "VARCHAR(20)", "Статус автомобиля", "CHECK"],
    ])
    add_table(doc, "Таблица 3 - Таблица client", ["Название атрибута", "Тип данных", "Назначение", "Ключевое поле"], [
        ["client_id", "SERIAL", "Уникальный идентификатор клиента", "Primary key"],
        ["full_name", "VARCHAR(100)", "ФИО клиента", "Нет"],
        ["phone", "VARCHAR(20)", "Контактный телефон", "UNIQUE"],
        ["address", "TEXT", "Адрес проживания", "Нет"],
        ["license_no", "VARCHAR(20)", "Номер водительского удостоверения", "UNIQUE"],
        ["created_at", "TIMESTAMPTZ", "Дата регистрации", "Нет"],
    ])
    add_table(doc, "Таблица 4 - Таблица rental", ["Название атрибута", "Тип данных", "Назначение", "Ключевое поле"], [
        ["rental_id", "SERIAL", "Уникальный идентификатор договора", "Primary key"],
        ["car_id", "INT", "Ссылка на автомобиль", "Foreign key"],
        ["client_id", "INT", "Ссылка на клиента", "Foreign key"],
        ["start_date", "DATE", "Дата начала аренды", "Нет"],
        ["end_date", "DATE", "Плановая дата возврата", "CHECK"],
        ["actual_return", "DATE", "Фактическая дата возврата", "Нет"],
        ["total_cost", "NUMERIC(10,2)", "Итоговая стоимость", "CHECK"],
        ["status", "VARCHAR(20)", "Статус договора", "CHECK"],
    ])
    add_table(doc, "Таблица 5 - Таблица app_user", ["Название атрибута", "Тип данных", "Назначение", "Ключевое поле"], [
        ["user_id", "SERIAL", "Уникальный идентификатор пользователя", "Primary key"],
        ["login", "VARCHAR(50)", "Логин пользователя", "UNIQUE"],
        ["full_name", "VARCHAR(100)", "ФИО или наименование пользователя", "Нет"],
        ["role", "VARCHAR(20)", "Роль пользователя", "CHECK"],
        ["password_hash", "TEXT", "Хеш пароля", "Нет"],
        ["password_salt", "TEXT", "Соль пароля", "Нет"],
        ["is_active", "BOOLEAN", "Признак активности учетной записи", "Нет"],
    ])
    add_table(doc, "Таблица 6 - Таблица audit_log", ["Название атрибута", "Тип данных", "Назначение", "Ключевое поле"], [
        ["audit_id", "BIGSERIAL", "Уникальный номер события", "Primary key"],
        ["changed_at", "TIMESTAMPTZ", "Дата и время операции", "Нет"],
        ["user_login", "TEXT", "Пользователь, выполнивший операцию", "Нет"],
        ["action", "TEXT", "Тип операции", "Нет"],
        ["entity", "TEXT", "Таблица", "Нет"],
        ["entity_id", "INT", "Идентификатор измененной записи", "Нет"],
        ["details", "TEXT", "Подробности изменения", "Нет"],
    ])

    add_heading(doc, "3.3 Создание объектов базы данных", 2)
    add_paragraph(doc, "В ходе выполнения индивидуального задания были созданы таблицы, индексы, представления, роли, политики безопасности и триггеры аудита. Основная схема расположена в PostgreSQL в схеме garanin_gi базы dbwork.")
    add_paragraph(doc, "Для ускорения выборок созданы индексы по внешним ключам, статусам и датам аренды. Для отчетности созданы представления v_rentals_full, v_available_cars и v_client_stats.")
    add_code(doc, """CREATE VIEW v_rentals_full AS
  SELECT r.rental_id, c.full_name AS client_name, c.phone AS client_phone,
         ca.license_plate, b.name AS brand_name, r.start_date, r.end_date,
         r.actual_return, r.total_cost, r.status
  FROM rental r
  JOIN client c ON c.client_id = r.client_id
  JOIN car ca ON ca.car_id = r.car_id
  JOIN brand b ON b.brand_id = ca.brand_id;""")

    add_heading(doc, "3.4 Руководство пользователя/администратора", 2)
    add_paragraph(doc, "Для запуска приложения необходимо открыть проект Windows Forms и выполнить команду dotnet run либо запустить собранный файл AutorentWinForms.exe. При запуске приложение подключается к серверу PostgreSQL edu-pg.itiscaf.ru, базе dbwork, схеме garanin_gi.")
    add_paragraph(doc, "После запуска отображается форма авторизации. В системе предусмотрены три типа пользователей:")
    add_table(doc, "Таблица 7 - Пользователи приложения", ["Логин", "Роль", "Доступ"], [
        ["garanin_gi", "admin", "Полный доступ ко всем функциям"],
        ["manager", "manager", "Работа с данными и отчетами"],
        ["shagavaliev_iv", "viewer", "Просмотр основных таблиц без отчетов, защиты и аудита"],
    ])
    add_paragraph(doc, "Администратор имеет полный доступ к данным, отчетам, журналу операций и разделу защиты. Менеджер может добавлять, редактировать и удалять рабочие записи, формировать отчеты и просматривать журнал. Пользователь viewer может только просматривать основные таблицы: клиенты, автомобили, марки и аренда.")
    add_paragraph(doc, "В основных вкладках приложения доступны функции поиска, просмотра, добавления, редактирования и удаления записей. Для формирования отчетов предусмотрен экспорт в HTML, TXT и CSV.")

    add_heading(doc, "3.5 Методы защиты данных в базе данных", 2)
    add_paragraph(doc, "В разработанной системе реализована комплексная защита информации на уровне базы данных и на уровне приложения. На уровне PostgreSQL используются ограничения целостности, роли, политики доступа, представления и триггеры журналирования.")
    add_bullets(doc, [
        "PRIMARY KEY исключает повторение идентификаторов записей;",
        "FOREIGN KEY обеспечивает ссылочную целостность между таблицами;",
        "UNIQUE запрещает повторение госномеров, телефонов, водительских удостоверений и названий марок;",
        "CHECK ограничивает диапазоны значений и допустимые статусы;",
        "ON DELETE RESTRICT запрещает удалять записи, используемые в связанных договорах;",
        "Row Level Security применяется для таблицы rental;",
        "audit_log фиксирует операции INSERT, UPDATE и DELETE.",
    ])
    add_paragraph(doc, "На уровне приложения реализованы вход по логину и паролю, разграничение интерфейса по ролям, параметризованные SQL-запросы через Npgsql и защита строки подключения через вынесение параметров в конфигурационный файл.")
    add_code(doc, """CREATE ROLE rent_admin NOLOGIN;
CREATE ROLE rent_manager NOLOGIN;
CREATE ROLE rent_viewer NOLOGIN;

GRANT SELECT, INSERT, UPDATE ON client, car, rental TO rent_manager;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO rent_viewer;

ALTER TABLE rental ENABLE ROW LEVEL SECURITY;
CREATE POLICY manager_access ON rental
  FOR ALL TO rent_manager USING (true) WITH CHECK (true);""")
    add_paragraph(doc, "Дополнительным методом защиты является журналирование операций. В базе данных создана таблица audit_log и триггеры, которые автоматически записывают сведения о действиях пользователя.")
    add_code(doc, """CREATE TRIGGER trg_audit_client
AFTER INSERT OR UPDATE OR DELETE ON client
FOR EACH ROW EXECUTE FUNCTION fn_autorent_audit('client_id');""")

    add_heading(doc, "4 Задание от предприятия", 1)
    add_paragraph(doc, "В рамках выполнения задания была разработана информационная система для автоматизации учета автопроката. Работа включала проектирование базы данных, создание таблиц и связей, разработку приложения для пользователей, настройку доступа к удаленному серверу PostgreSQL и оформление отчетной документации.")
    add_paragraph(doc, "Полученный программный продукт может использоваться менеджером автопроката для ведения клиентов, автомобилей, марок и договоров аренды. Администратор получает возможность контролировать журнал операций и параметры защиты, а пользователь просмотра может анализировать данные без риска их изменения.")

    add_heading(doc, "Заключение", 1)
    add_paragraph(doc, "В результате выполнения задания по модулю ПМ.11 были выполнены все основные этапы проектирования и разработки базы данных и приложения для предметной области «Автопрокат». Была разработана нормализованная структура базы данных PostgreSQL, включающая таблицы, связи, ограничения, индексы, представления и триггеры.")
    add_paragraph(doc, "На этапе реализации было создано Windows Forms приложение на языке C#, обеспечивающее авторизацию пользователей, просмотр, поиск, ввод, редактирование и удаление данных, а также формирование отчетных документов. Приложение подключается к удаленной базе данных PostgreSQL и использует параметризованные запросы.")
    add_paragraph(doc, "В системе реализованы методы защиты данных: разграничение ролей, ограничения целостности, проверка прав на уровне интерфейса, журналирование операций и использование защищенной конфигурации подключения. Таким образом, задание по модулю ПМ.11 выполнено в полном объеме.")

    add_heading(doc, "Приложение А", 1)
    p = doc.add_paragraph("(обязательное)")
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    add_paragraph(doc, "Рисунок 1 - ER модель базы данных «Автопрокат»")
    add_paragraph(doc, "Рисунок 2 - Реляционная модель базы данных «Автопрокат»")

    add_heading(doc, "Приложение Б", 1)
    p = doc.add_paragraph("(обязательное)")
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    add_paragraph(doc, "Рисунок 3 - Создание таблиц brand, car, client, rental")
    add_paragraph(doc, "Рисунок 4 - Создание представлений v_rentals_full, v_available_cars, v_client_stats")
    add_paragraph(doc, "Рисунок 5 - Создание ролей и прав доступа")
    add_paragraph(doc, "Рисунок 6 - Создание триггеров аудита")
    add_code(doc, Path("database_security_postgresql.sql").read_text(encoding="utf-8")[:12000])

    doc.save(OUT)
    print(OUT)


if __name__ == "__main__":
    main()
